diff --git a/LICENSE.txt b/LICENSE.txt old mode 100755 new mode 100644 diff --git a/Makefile b/Makefile index 8556dc0..4a4d37b 100644 --- a/Makefile +++ b/Makefile @@ -4,11 +4,14 @@ # CFLAGS = -O -build: cvbasic.o node.o driver.o cpuz80.o cpu6502.o cpu9900.o - @$(CC) cvbasic.o node.o driver.o cpuz80.o cpu6502.o cpu9900.o -o cvbasic +cvbasic: cvbasic.o node.o driver.o cpuz80.o cpu6502.o cpu9900.o + @$(CC) cvbasic.o node.o driver.o cpuz80.o cpu6502.o cpu9900.o -o $@ $(LDFLAGS) -check: build - @./cvbasic examples/viboritas.bas /tmp/viboritas.asm +check: cvbasic + @./$< examples/viboritas.bas /tmp/viboritas.asm + @./$< --sms examples/viboritas_sms.bas /tmp/viboritas_sms.asm + @./$< --nes examples/viboritas_nes.bas /tmp/viboritas_nes.asm + @./$< --msx2 examples/viboritas_msx2.bas /tmp/viboritas_msx2.asm clean: @rm cvbasic cvbasic.o node.o driver.o cpuz80.o cpu6502.o cpu9900.o diff --git a/README.md b/README.md index 9e6ec37..7b2eac0 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,12 @@ -# CVBasic compiler v0.7.1 -*(c) Copyright 2024 Óscar Toledo Gutiérrez* +# CVBasic compiler v0.9.2 +*(c) Copyright 2024-2026 Óscar Toledo Gutiérrez* *https://nanochess.org/* -CVBasic is a BASIC language cross-compiler with a syntax alike to QBasic originally written for the Colecovision video game console. - -The CVBasic compiler can create programs up to 1 MB using the BANK statements (using 16K bank switching on most platforms, 8k on TI-99/4A). - -Later it was extended to support the following platforms: +CVBasic is a BASIC language cross-compiler with a syntax alike to QBasic. It supports the following platforms: +* Colecovision (supporting bank switching with Megacart mapper) * Sega SG-1000 / SC-3000 (supporting bank switching with Sega mapper) -* MSX 1 (supporting bank switching with ASCII16 mapper) +* MSX 1 and MSX 2 (supporting bank switching with ASCII16 mapper) * Texas Instruments TI-99/4A (courtesy of @tursilion, supporting bank switching) * Spectravideo SVI-318 / 328. * Sord M5. @@ -19,9 +16,17 @@ Later it was extended to support the following platforms: * Casio PV2000. * Hanimex/Soundic Pencil II. * NABU PC. +* Sega Master System (supporting bank switching with Sega mapper) +* NES/Famicom (supporting bank switching using mapper 30 - UNROM 512) + +The CVBasic compiler can create programs up to 1 MB using the BANK statements (using 16K bank switching on most platforms, 8k on TI-99/4A). One of the advantages of using CVBasic is that all the programs can be compiled for all the platforms with mostly no modifications at all. Although the compiler started supporting only Z80, now this includes the 6502 based Creativision, and TMS9900 based TI-99/4A. This way it achieves a truly portable BASIC across the common theme: the video processor Texas Instruments TMS9128/9129. +Porting to MSX2 is relatively easy. You only need to use MODE 4 for enabling the extended video mode, and create 16-byte color tables for sprites, and use these with DEFINE SPRITE COLOR. + +Currently, Sega Master System and NES/Famicom are the most different in video terms, although the compiler shares the core, the sprites are 8x16 and each pixel can have a different color, so the games aren't directly portable, but very easily translated. The examples include SMS and NES/Famicom versions for every game so you can see the required changes. + The following files compose the compiler: cvbasic.h The CVBasic compiler global definitions. @@ -40,33 +45,65 @@ The following files compose the compiler: cvbasic_prologue.asm Prologue file needed for compiled programs. cvbasic_epilogue.asm Epilogue file needed for compiled programs. - cvbasic_6502_prologue.asm Prologue file needed for 6502 compiled programs. - cvbasic_6502_epilogue.asm Epilogue file needed for 6502 compiled programs. + cvbasic_6502_prologue.asm Prologue file needed for Creativision compiled programs. + cvbasic_6502_epilogue.asm Epilogue file needed for Creativision compiled programs. cvbasic_9900_prologue.asm Prologue file needed for TMS9900 compiled programs. cvbasic_9900_epilogue.asm Epilogue file needed for TMS9900 compiled programs. + cvbasic_nes_prologue.asm Prologue file needed for NES/Famicom compiled programs. + cvbasic_nes_epilogue.asm Epilogue file needed for NES/Famicom compiled programs. manual.txt English manual for CVBasic README.md This file examples/bank.bas Bank-switching example. + examples/bank_nes.bas Bank-switching example (NES/Famicom) + examples/bank_sms.bas Bank-switching example (Sega Master System) examples/brinquitos.bas Jumping game. + examples/brinquitos_nes.bas Jumping game (NES/Famicom) + examples/brinquitos_sms.bas Jumping game (Sega Master System) examples/controller.bas Controller test. + examples/controller_nes.bas Controller test (NES) + examples/cats_sms.bas Cats example (Sega Master System) examples/demo.bas Demo of graphics. + examples/demo_nes.bas Demo of graphics (NES/Famicom) + examples/demo_sms.bas Demo of graphics (Sega Master System) examples/face_joystick.bas Moving face with joystick. + examples/face_joystick_nes.bas Moving face with joystick (NES/Famicom) + examples/face_joystick_sms.bas Moving face with joystick (Sega Master System) examples/happy_face.bas Bouncing face. + examples/happy_face_nes.bas Bouncing face (NES/Famicom). + examples/happy_face_sms.bas Bouncing face (Sega Master System). examples/music.bas Music example. examples/oscar_compressed.bas High-resolution graphics example compressed with Pletter. + examples/oscar_compressed_sms.bas High-resolution graphics example compressed with Pletter (Sega Master System) examples/oscar.bas High-resolution graphics example. + examples/oscar_nes.bas High-resolution graphics example (NES/Famicom) + examples/oscar_sms.bas High-resolution graphics example (Sega Master System) + examples/palette_msx2.bas Example of palette support in MSX2 examples/portrait.bas Data used by demo.bas + examples/portrait_sms.bas Data used by demo_sms.bas examples/space_attack.bas Game example. + examples/space_attack_nes.bas Game example (NES/Famicom) + examples/space_attack_sms.bas Game example (Sega Master System) + examples/spinner.bas Spinner / Roller Controller example (Colecovision) examples/test1.bas Moving stars. examples/test2.bas Arithmetic test. examples/test3.bas Shows usage of SELECT CASE / END CASE + examples/test3_sms.bas Shows usage of SELECT CASE / END CASE (Sega Master System) + examples/test4.bas Benchmark and test of multiplication/division + examples/test5.bas Example of using the preprocessor examples/varptr.bas VARPTR example for redefining graphics. - examples/vgm.bas VGM audio player. + examples/varptr_sms.bas VARPTR example for redefining graphics (Sega Master System) + examples/vgm.bas VGM audio player for SN76489. + examples/vgm_ay.bas VGM audio player for AY-3-8910. + examples/vgm_nes.bas VGM audio player for NES/Famicom. examples/viboritas.bas Game example. + examples/viboritas_msx2.bas Game example (MSX2) + examples/viboritas_nes.bas Game example (NES/Famicom) + examples/viboritas_sms.bas Game example (Sega Master System) examples/vramcopy.bas VRAM copy example. + examples/vramcopy_sms.bas VRAM copy example (Sega Master System) ### Usage guide @@ -83,7 +120,7 @@ Using CVBasic to compile a Sega SG1000/SC3000 program: cvbasic --sg1000 game.bas game.asm gasm80 game.asm -o game.rom -Using CVBasic to compile a MSX program for 8K RAM (wider compatibility) and 16K RAM: +Using CVBasic to compile an MSX program for 8K RAM (wider compatibility) and 16K RAM: cvbasic --msx game.bas game.asm gasm80 game.asm -o game.rom @@ -91,6 +128,24 @@ Using CVBasic to compile a MSX program for 8K RAM (wider compatibility) and 16K cvbasic --msx -ram16 game.bas game.asm gasm80 game.asm -o game.rom + cvbasic --msx -ram16 -konami game.bas game.asm + gasm80 game.asm -o game.rom + +For bank-switched programs the default mapper is ASCII16, but you have the option of using a standard Konami 8K mapper using the -konami switch. + +Using CVBasic to compile an MSX2 program for 8K RAM (wider compatibility) and 16K RAM: + + cvbasic --msx2 game.bas game.asm + gasm80 game.asm -o game.rom + + cvbasic --msx2 -ram16 game.bas game.asm + gasm80 game.asm -o game.rom + + cvbasic --msx2 -ram16 -konami game.bas game.asm + gasm80 game.asm -o game.rom + +For bank-switched programs the default mapper is ASCII16, but you have the option of using a standard Konami 8K mapper using the -konami switch. + Using CVBasic to compile a Colecovision Super Game Module program: cvbasic --sgm game.bas game.asm @@ -101,7 +156,7 @@ Using CVBasic to compile a Spectravideo SVI-318/328 program: cvbasic --svi game.bas game.asm gasm80 game.asm -o game.rom -Using CVBasic to compile a Sord M5 program: +Using CVBasic to compile a Sord M5 program (16K ROM): cvbasic --sord game.bas game.asm gasm80 game.asm -o game.rom @@ -119,7 +174,9 @@ Using CVBasic to compile a VTech Creativision (Dick Smith's Wizzard / Vtech Lase cvbasic --creativision game.bas game.asm gasm80 game.asm -o game.rom -You can use the -rom16 option to generate a 16K ROM instead of a 32K ROM, and it is provided utility/switch.c to switch the two 8K banks in a 16K ROM and run it with MAME, as MAME doesn't yet support 32K ROM. For 32K ROM you can use the Creativision emulator. +You can use the -rom16 option to generate a 16K ROM instead of a 32K ROM, and it is provided utility/switch.c to switch the two 8K banks in a 16K ROM and run it with MAME, as MAME doesn't yet support 32K ROM. For 32K ROM you can use the [Creativision emulator](https://sourceforge.net/projects/creativisionemulator/), and the command line is: + + creativision -g -b ..\BIOS\BIOSCV.ROM -r game.rom Using CVBasic to compile a Hanimex/Soundic Pencil II program (almost exactly like a Colecovision, but with 2K of RAM and different cartridge header): @@ -131,7 +188,7 @@ Using CVBasic to compile a Tatung Einstein program: cvbasic --einstein game.bas game.asm gasm80 game.asm -o game.com -Using CVBasic to compile a Casio PV-2000 program: +Using CVBasic to compile a Casio PV-2000 program (16k ROM): cvbasic --pv2000 game.bas game.asm gasm80 game.asm -o game.rom @@ -155,29 +212,44 @@ Using CVBasic to compile a NABU PC program: cvbasic --nabu -cpm game.bas game.asm gasm80 game.asm -o game.com -The .npz file is directlty usable by [MAME NABU port](https://github.com/ontheslab/nabu-mame-builds/releases) (put it in the nabu directory). The 000001.nabu name is important or it doesn't work. I've used the script _boot-built-in-adapter-local.sh_ +The .npz file is directly usable by [MAME NABU port v1.0.2](https://github.com/ontheslab/nabu-mame-builds/releases) (put it in the nabu directory). The 000001.nabu name is important or it doesn't work. I've used the script _boot-built-in-adapter-local.sh_ I don't have tested the COM files for NABU CP/M yet. +Using CVBasic to compile a Sega Master System program: + + cvbasic --sms game.bas game.asm + gasm80 game.asm -o game.sms -sms + +The -sms option of gasm80 will generate automatically a valid checksum so the game can run on a real console. + +Using CVBasic to compile a NES/Famicom program: + + cvbasic --nes game.bas game.asm + gasm80 game.asm -o game.nes + + ### Notes -The current official version is v0.7.0. +The current official version is v0.9.0. All platforms have been tested in emulation. -* Colecovision and MSX have been tested in real hardware by myself. +* Colecovision, MSX, and NES have been tested in real hardware by myself. * Sega SG1000/SC3000 tested in real hardware by aotta. * Spectravideo SVI-318/328 tested in real hardware by Tony Cruise. * Creativision / Dick Smith's Wizzard tested in real hardware by Scouter3d. -* NABU tested in real hardware by Sektor. +* NABU tested in real hardware by Sektor and rietveld. +* Sega Master System tested in real hardware and Sega Genesis/Megadrive by myself. +* TI-99/4A tested in real hardware by retroclouds. Untested: * DEFINE VRAM READ is only tested for Colecovision/MSX, please test in your real hardware to check that the timing is right. -MSX controller support only handles the two joysticks and keyboard arrows (plus Space and M for buttons). The keys 0-9, Backspace and Return emulate the Colecovision keypad (CONT1.KEY only). +MSX/MSX2 controller support only handles the two joysticks and keyboard arrows (plus Space and M for buttons). The keys 0-9, Backspace and Return emulate the Colecovision keypad (CONT1.KEY only). -The Sega SG1000 doesn't have any keypad, so CONT1.KEY and CONT2.KEY aren't operative, but the support includes compatibility with Sega SC3000 computer, and the keyboard can be used as first controller (code contributed by SiRioKD) and for CONT1.KEY using the keys 0-9, Delete and CR. +The Sega SG1000 and Sega Master System doesn't have any keypad, so CONT1.KEY and CONT2.KEY aren't operative, but the support includes compatibility with Sega SC3000 computer, and the keyboard can be used as first controller (code contributed by SiRioKD) and for CONT1.KEY using the keys 0-9, Delete and CR. The Spectravideo SVI-328 only has one button in the joystick. The keyboard can be used for the second button (letter M) and to have keypad (CONT1.KEY only) using the keys 0-9, Backspace and Return. @@ -191,11 +263,81 @@ The Creativision can only use binaries up to 32 kb, the joysticks are controller The TI-99/4A can only generate non-banked binaries up to 24 kb. When banking, the fixed space is 24k and pages are 8k. Both joysticks are supported with a single button. The second button is simulated on the keyboard with control for player 1 and fctn for player 2. CONT1.KEY will also return uppercase ASCII characters from the keyboard in addition to the stock 0-9, #, * for compatibility with Coleco programs. No keypad is implemented for controller 2 - only the joystick. The program supports FCTN-= (Alt-= on PC emulation) to reset. -The NABU PC can only use binaries up to 32 kb, joysticks can be used for both controllers, keyboard arrows are handled as controller 1, and it can also emulate the Colecovision keypad (CONT1.KEY only) using the keys 0-9, Del and Enter. +The NABU PC can only use binaries up to 32 kb, joysticks can be used for both controllers, keyboard arrows are handled as controller 1 (<||| is the primary button and |||> is the secondary button), and it can also emulate the Colecovision keypad (CONT1.KEY only) using the keys 0-9, Del and Enter. + +The NES/Famicom translates Select to CONT.KEY = 10, and Start to CONT.KEY = 11. Many people is developing games using CVBasic, feel free to check some of these examples at the [AtariAge Colecovision Programming forum](https://forums.atariage.com/forum/55-colecovision-programming/) +### People using CVBasic + +There are some very good examples of games developed with CVBasic. + +* Airlock II game by OriginalJohn. Demo available from [https://forums.atariage.com/topic/379104-cvbasic-airlock-ii-in-progress/](https://forums.atariage.com/topic/379104-cvbasic-airlock-ii-in-progress/) + +* Akalabeth by nanochess. Video available at [https://www.youtube.com/watch?v=Shry64lalDU](https://www.youtube.com/watch?v=Shry64lalDU) + +* astronave by Haranni. Demo available from [https://forums.atariage.com/topic/381647-astronave/](https://forums.atariage.com/topic/381647-astronave/) + +* Barbarricade by Jess Ragan. Demo available from [https://forums.atariage.com/topic/390220-barbarricade-the-brick-breaking-game-where-the-bricks-break-you/](https://forums.atariage.com/topic/390220-barbarricade-the-brick-breaking-game-where-the-bricks-break-you/) + +* Beachhead by Eric Damain. Demo screenshots [https://forums.atariage.com/topic/373822-beach-head-dev-log-cvbasic/](https://forums.atariage.com/topic/373822-beach-head-dev-log-cvbasic/) + +* Boom by Atarius Maximus for NES/Famicom. Also in the _contrib_ directory. [https://forums.atariage.com/topic/384096-colecovision-fantalicious-basic-compiler-cvbasic-v090-now-with-nesfamicom-support/page/2/#findComment-5769427](https://forums.atariage.com/topic/384096-colecovision-fantalicious-basic-compiler-cvbasic-v090-now-with-nesfamicom-support/page/2/#findComment-5769427) + +* Camelot Knights by nanochess. Available from [https://forums.atariage.com/topic/363252-camelot-knights-game/](https://forums.atariage.com/topic/363252-camelot-knights-game/) + +* Colecovania demo by Skywaffle. Video at [https://www.youtube.com/watch?v=ixlAEZ0GSis&ab_channel=MatthewKiehl](https://www.youtube.com/watch?v=ixlAEZ0GSis&ab_channel=MatthewKiehl) + +* Crackpots by Eric Damain. Available from [https://forums.atariage.com/topic/388119-crackpots/](https://forums.atariage.com/topic/388119-crackpots/) + +* Donkey.BAS by Claus Bækkel. Available for purchase from [https://forums.atariage.com/topic/366153-donkeybas-game-coded-in-cvbasic-0500/](https://forums.atariage.com/topic/366153-donkeybas-game-coded-in-cvbasic-0500/) + +* Dragon's Descent by Revontuli. Demo available from [https://forums.atariage.com/topic/388152-dragons-descent-for-the-colecovision/](https://forums.atariage.com/topic/388152-dragons-descent-for-the-colecovision/) + +* Duocide by Jess Ragan. Demo available from [https://forums.atariage.com/topic/379738-duocide-the-side-switching-shooter/](https://forums.atariage.com/topic/379738-duocide-the-side-switching-shooter/) + +* Eye Brawls by Jess Ragan. Demo available from [https://forums.atariage.com/topic/387711-eye-brawls-the-rev-eye-val/](https://forums.atariage.com/topic/387711-eye-brawls-the-rev-eye-val/) + +* Gorilla.BAS by nanochess. Available from [https://forums.atariage.com/topic/370716-gorillabas-for-colecovision/](https://forums.atariage.com/topic/370716-gorillabas-for-colecovision/) + +* Gun Fight by Eric Damain. Video at [https://forums.atariage.com/topic/379102-gun-fight-dev-log-cvbasic/](https://forums.atariage.com/topic/379102-gun-fight-dev-log-cvbasic/) + +* Hamurabi by Eric Damain. Available from [https://forums.atariage.com/topic/380392-hamurabi-dev-log-cvbasic/](https://forums.atariage.com/topic/380392-hamurabi-dev-log-cvbasic/) + +* Jewels by DannyVdH. Available from [https://forums.atariage.com/topic/390921-jewels-game-build-in-adam-emulator-plugin-tools-cvbasic-new-graphics-editor/](https://forums.atariage.com/topic/390921-jewels-game-build-in-adam-emulator-plugin-tools-cvbasic-new-graphics-editor/) + +* Jurl by Tonsomo Entertainment. Available from [https://tonsomo.itch.io/jurl-colecovision-edition](https://tonsomo.itch.io/jurl-colecovision-edition) + +* Manhole by Eric Damain. Available from [https://electric-dreams.itch.io/manhole-for-colecovision](https://electric-dreams.itch.io/manhole-for-colecovision) + +* Metro Wars by nanochess (MSX2). ROM available for download from [https://www.msxdev.org/2026/03/08/msxdev25-36-metro-wars/](https://www.msxdev.org/2026/03/08/msxdev25-36-metro-wars/) + +* Monkey Moon by nanochess, example from the book **Programming Games for Colecovision**. Available from [https://forums.atariage.com/topic/365456-the-mystery-of-monkey-moon/#findComment-5478249](https://forums.atariage.com/topic/365456-the-mystery-of-monkey-moon/#findComment-5478249) + +* Oil Panic by Eric Damain. Availabel from [https://forums.atariage.com/topic/390270-another-gw-adaptation-oil-panic/](https://forums.atariage.com/topic/390270-another-gw-adaptation-oil-panic/) + +* Operation Hibernation by Jess Ragan. Available from [https://forums.atariage.com/topic/383365-operation-hibernation-nearing-completion/](https://forums.atariage.com/topic/383365-operation-hibernation-nearing-completion/) + +* Pipe Dreams by visrealm. Available from [https://forums.atariage.com/topic/382747-pipe-dreams-clone-for-the-colecovision-and-others-cvbasic/](https://forums.atariage.com/topic/382747-pipe-dreams-clone-for-the-colecovision-and-others-cvbasic/) + +* Rogue-like Wip by Skywaffle. Available from [https://forums.atariage.com/topic/372717-colecovision-rogue-like-wip/](https://forums.atariage.com/topic/372717-colecovision-rogue-like-wip/) + +* Solar Fox 2 by Jess Ragan. Available from [https://forums.atariage.com/topic/382881-solar-fox-2-is-finished-link-inside/](https://forums.atariage.com/topic/382881-solar-fox-2-is-finished-link-inside/) + +* Space Taxi by Eric Damain. Available from [https://forums.atariage.com/topic/388538-hey-taxi/](https://forums.atariage.com/topic/388538-hey-taxi/) + +* Sub Hunt by Eric Damain. Demo videos [https://forums.atariage.com/topic/374259-sub-hunt-dev-log-cvbasic/](https://forums.atariage.com/topic/374259-sub-hunt-dev-log-cvbasic/) + +* SWOPEM by chalkyw64 (MSX). Available from [https://forums.atariage.com/topic/387521-swopem-a-simple-match3-game-for-msx-and-colecovision/](https://forums.atariage.com/topic/387521-swopem-a-simple-match3-game-for-msx-and-colecovision/) + +* Taz by Eric Damain. Demo available from [https://electric-dreams.itch.io/taz](https://electric-dreams.itch.io/taz) + +* Warren Cave by Eric Damain. Demo available from [https://forums.atariage.com/topic/389458-a-text-adventure-game-test-cvbasic/](https://forums.atariage.com/topic/389458-a-text-adventure-game-test-cvbasic/) + +* Whack'em Smack'em Byron by Jess Ragan. Available from [https://forums.atariage.com/topic/364885-whack-em-smack-em-byron-dev-log/page/3/#findComment-5483961](https://forums.atariage.com/topic/364885-whack-em-smack-em-byron-dev-log/page/3/#findComment-5483961) + ### Supporting the developer If you find CVBasic useful, please show your appreciation making a donation via Paypal ($9 USD suggested) to b-i+y-u+b-i (at) gmail.com @@ -206,13 +348,23 @@ You can also get my book **Programming Games for Colecovision** including an int The foreword is written by the legendary David R. Megarry, programmer of Zaxxon™ for Colecovision, and creator of the Dungeon!™ Board game. -All the games in the book will compile for **all** the platforms. +All the games in the book will compile for **all** the platforms, except Sega Master System and NES/Famicom (because the different video processor, but can be adapted), and Dungeon Warrior cannot compile for Sord M5 and Casio PV2000 because the small 16KB ROM size. * [Programming Games for Colecovision, paperback, 250 pages](https://www.lulu.com/shop/oscar-toledo-gutierrez/programming-games-for-colecovision/paperback/product-95qvzj8.html?page=1&pageSize=4) * [Programming Games for Colecovision, hardcover, 250 pages](https://www.lulu.com/shop/oscar-toledo-gutierrez/programming-games-for-colecovision/hardcover/product-84nm767.html?page=1&pageSize=4) * [Programming Games for Colecovision, PDF ebook, 250 pages](https://nanochess.org/store.html) +### Toolkits + +Several utilities which can be used with CVBasic have emerged for graphics design, music, and sound effects. + +* Amy's CVPaint Studio [https://amypurple.github.io/AmysCVPaintStudio/](https://amypurple.github.io/AmysCVPaintStudio/) + +* Kamshaft's CV Toolkit, including Sprite Tool, Tile Creator, Sound Creator, and Music Tracker [https://cvaddict.com/tools/](https://cvaddict.com/tools/) + + ### Acknowledgments -Thanks to the following members of Atariage for contributing valuable suggestions: abeker, aotta, ARTRAG, atari2600land, carlsson, chalkyw64, CrazyBoss, drfloyd, gemintronic, Jess Ragan, Kamshaft, Kiwi, pixelboy, SiRioKD, Tarzilla, Tony Cruise, tursilion, visrealm, wavemotion, and youki. +Thanks to the following members of Atariage for contributing valuable suggestions: abeker, acadiel, aotta, ARTRAG, atari2600land, Atarius Maximus, carlsson, chalkyw64, CrazyBoss, drfloyd, gemintronic, Jess Ragan, Kamshaft, Kiwi, pixelboy, Revontuli, SiRioKD, Tarzilla, Tony Cruise, tursilion, unhuman, + visrealm, wavemotion, and youki. diff --git a/contrib/Boom_NES_v1.0.bas b/contrib/Boom_NES_v1.0.bas new file mode 100644 index 0000000..dd707f6 --- /dev/null +++ b/contrib/Boom_NES_v1.0.bas @@ -0,0 +1,2125 @@ +' BOOM! NES v1.0.25 +' Compiles with CVBasic v0.90 +' Steve Engelhardt +' 12/29/2025 + +' -- Global Palettes -- +PALETTE 0, $0F +PALETTE 1, $1A +PALETTE 2, $01 +PALETTE 3, $00 + +' -- Sprite Palettes -- +' Palette 0: Bomber & General (Black, White, Orange) +PALETTE 16, $0F +PALETTE 17, $0F +PALETTE 18, $30 ' White (Face) +PALETTE 19, $27 + +' Palette 1: Bombs (Black, White, Red) +PALETTE 20, $0F +PALETTE 21, $0F +PALETTE 22, $00 +PALETTE 23, $30 + +' Palette 2: Score (Orange) +PALETTE 24, $0F +PALETTE 25, $0F +PALETTE 26, $0F +PALETTE 27, $27 + +' Palette 3: Buckets (Black, Blue, Brown) +PALETTE 28, $0F ' Transparent +PALETTE 29, $0F ' 1 = Black (Outline) +PALETTE 30, $12 ' 2 = Blue (Water) +PALETTE 31, $07 ' 3 = Dark Brown (Body) + +' -- Variables -- + +' Bomb Arrays (Up to 8 active bombs) +DIM bomb_x(8) ' Horizontal position of each active bomb +DIM bomb_y(8) ' Vertical position of each active bomb +DIM bomb_s(8) ' Status of bomb slot (0=Inactive, 1=Active) + +' Player Bucket (Paddle) +DIM x_bucket ' Current X position of player buckets +DIM y_bucket ' Vertical anchor for buckets (usually static) + +' Bomber (Bomber) +DIM x_bomber ' Current X position of the Bomber +DIM y_bomber ' Current Y position of the Bomber +DIM bomber_dir ' Movement direction (0=Left, 1=Right) +DIM bomber_timer ' Timer used for random direction changes +DIM bomber_attr ' Sprite attribute (used for horizontal flipping) + +' Loop & Utility +DIM digit ' Temporary variable for digit calculations +DIM c ' Primary loop counter (Universal) +DIM lives ' Remaining buckets (1 to 3) +DIM r ' Secondary loop counter / temp math flag +DIM spawn_timer ' Frames until the next bomb can drop +DIM wait_timer ' General purpose delay timer + +' Drawing & Flicker Control +DIM col1_x ' Sprite column 1 X offset +DIM col2_x ' Sprite column 2 X offset +DIM col3_x ' Sprite column 3 X offset +DIM col4_x ' Sprite column 4 X offset +DIM b_vis_1 ' Visibility/Y-pos for bucket row 1 +DIM b_vis_2 ' Visibility/Y-pos for bucket row 2 +DIM b_vis_3 ' Visibility/Y-pos for bucket row 3 + +' Difficulty & Game Loop +DIM bomb_speed ' Falling speed (pixels per frame) +DIM max_spawn_delay ' Base delay between bomb drops for current wave +DIM rng_seed ' Rolling counter used for random numbers +DIM hit_detected ' Flag (1 if bomb caught, 0 if not) +DIM difficulty ' Skill level (0=Easy, 1=Std, 2=Hard) +DIM input_delay ' Prevents rapid-fire menu scrolling +DIM hit_width ' Collision width based on bucket size +DIM right_limit ' Screen boundary based on bucket size + +' Velocity / Smooth Movement +DIM b_dir ' Current velocity direction (0=none, 1=L, 2=R) +DIM b_speed ' Current pixel-speed of momentum +DIM b_subpixel ' Fractional movement for smooth acceleration + +' Wave Logic +DIM wave ' Current wave number +DIM bombs_remaining ' Bombs left to drop in current wave +DIM active_bombs ' Current count of bombs on screen +DIM found_slot ' Used to find available array index for new bomb + +' Current Score Digit Variables (for display) +DIM d_tthou ' Ten-Thousands digit +DIM d_thou ' Thousands digit +DIM d_hund ' Hundreds digit +DIM d_tens ' Tens digit +DIM d_ones ' Ones digit +DIM temp_val ' Temp storage for scoring and PPU logic + +' High Score Digit Variables +DIM h_tthou ' High Score: Ten-Thousands +DIM h_thou ' High Score: Thousands +DIM h_hund ' High Score: Hundreds +DIM h_tens ' High Score: Tens +DIM h_ones ' High Score: Ones + +' Splash Animation Arrays +DIM splash_x(3) ' Horizontal offset for catch splash +DIM splash_y(3) ' Vertical offset for catch splash +DIM splash_t(3) ' Frames remaining for splash animation + +' Menu & Rotation +DIM control_type ' 0=Digital, 1=Velocity +DIM menu_sel ' Current menu cursor selection +DIM s_row1 ' Sprite index for bucket row 1 (Priority rotation) +DIM s_row2 ' Sprite index for bucket row 2 (Priority rotation) +DIM s_row3 ' Sprite index for bucket row 3 (Priority rotation) +DIM s_ex1 ' Extra sprite for Easy mode bucket expansion +DIM s_ex2 ' Extra sprite for Easy mode bucket expansion +DIM s_ex3 ' Extra sprite for Easy mode bucket expansion +DIM last_wave ' Records wave reached before Game Over + +' Music Engine +DIM music_step ' Current step in the sequence (0-3 or 0-15) +DIM music_timer ' Frames remaining until next music step +DIM music_pattern ' Current pattern state (0=Intro, 1=Main, 2=Bridge) +DIM melody_index ' Note table index for Square 1 +DIM harmony_index ' Note table index for Square 2 +DIM bass_index ' Note table index for Triangle +DIM drum_index ' Sound type for Noise channel (1=Snare, 2=Kick) +DIM duration ' Frame length of the current note +DIM music_active ' Music toggle (1=Play, 0=Silent) +DIM sfx_timer ' Frames remaining for active Sound Effect + + +' ========================================================================= +' BOOT +' ========================================================================= +boot: + rng_seed = 0 ' Initialize random seed (will increment during title loop) + difficulty = 1 ' Default to "Standard" difficulty for a balanced first game + control_type = 0 ' Default to "Digital" controls as it's most familiar to players + menu_sel = 0 ' Start the menu cursor on the first row (Skill selection) + + ' -- Initialize Music State -- + music_active = 1 ' Enable the music engine so the title theme plays immediately + music_pattern = 0 ' Start with the Intro sequence rather than the main loop + music_timer = 0 ' Set to 0 so the engine triggers the first note instantly + music_step = 0 ' Start at the very first note (Step 0) of the Intro + +' ========================================================================= +' TITLE SCREEN +' ========================================================================= +title_screen: + SCREEN DISABLE + CLS + + ' -- Title Palettes -- + ' Palette 0: Borders & Buildings + PALETTE 0, $0F ' Slot 0 (Transparent/Global Back - Black) + PALETTE 1, $16 ' Slot 1 (Outer Border "*" - Dark Blue) + PALETTE 2, $01 ' Slot 2 (Medium Blue/Navy) + PALETTE 3, $10 ' Slot 3 (Light Grey) + + ' Palette 1: BOOM! Logo + PALETTE 4, $0F ' Slot 0 (Black) + PALETTE 5, $16 ' Slot 1 (Logo - Dark Blue) + PALETTE 6, $01 ' Slot 2 (Navy) + PALETTE 7, $10 ' Slot 3 (Light Grey) + + ' Palette 2: Box Borders + PALETTE 8, $0F ' Slot 0 (Black) + PALETTE 9, $0F ' Slot 1 (Inner Borders "\201" - Black) + PALETTE 10, $01 ' Slot 2 (Navy) + PALETTE 11, $10 ' Slot 3 (Light Grey) + + ' Palette 3: UI Text + PALETTE 12, $0F ' Slot 0 (Black) + PALETTE 13, $10 ' Slot 1 (Text - Light Grey) + PALETTE 14, $01 ' Slot 2 (Navy) + PALETTE 15, $10 ' Slot 3 (Light Grey) + + ' Sprite Palette 0: Title Screen Bomber (Bomber) + PALETTE 16, $0F ' Slot 0 (Transparent) + PALETTE 17, $0F ' Slot 1 (Black Outline) + PALETTE 18, $30 ' Slot 2 (Face/Eyes - White) + PALETTE 19, $27 ' Slot 3 (Clothes/Hat - Orange) + + ' -- DRAWING PHASE 1: Top Borders -- + PRINT AT 0, "********************************" + PRINT AT 32, "********************************" + PRINT AT 64, "*((((((((((((((((((((((((((((((*" + PRINT AT 96, "*((((((((((((((((((((((((((((((*" + PRINT AT 128, "*((((((((((((((((((((((((((((((*" + PRINT AT 160, "*((((((((((((((((((((((((((((((*" + + WAIT + + ' -- DRAWING PHASE 2: Logo Area -- + PRINT AT 192, "*((((((((((((((((((((((((((((((*" + PRINT AT 224, "*((((((((((((((((((((((((((((((*" + PRINT AT 256, "*((((((((((((((((((((((((((((((*" + PRINT AT 288, "*((((((((((((((((((((((((((((((*" + PRINT AT 320, "*((((((((((((((((((((((((((((((*" + PRINT AT 352, "*((((((((((((((((((((((((((((((*" + PRINT AT 384, "*((((((((((((((((((((((((((((((*" + + WAIT + + ' -- DRAWING PHASE 3: Score Box -- + PRINT AT 416, "*(((((\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201(((((*" + PRINT AT 448, "*(((((\201 \201(((((*" + PRINT AT 480, "*(((((\201 \201(((((*" + PRINT AT 512, "*(((((\201 \201(((((*" + PRINT AT 544, "*(((((\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201(((((*" + + PRINT AT 576, "*((((((((((((((((((((((((((((((*" + + WAIT + + ' -- DRAWING PHASE 4: Difficulty Box -- + PRINT AT 608, "*(((((\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201(((((*" + PRINT AT 640, "*(((((\201 \201(((((*" + PRINT AT 672, "*(((((\201 \201(((((*" + PRINT AT 704, "*(((((\201 \201(((((*" + PRINT AT 736, "*(((((\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201\201(((((*" + + WAIT + + ' -- DRAWING PHASE 5: Bottom Closure -- + PRINT AT 768, "*((((((((((((((((((((((((((((((*" + PRINT AT 800, "********************************" + + WAIT + + ' Row 27 (864 + 4): Copyright shifted up one row + PRINT AT 868, "\127 2026 STEVE ENGELHARDT" + + ' Sprites for Bomber + SPRITE 0, 16, 110, 128, 0 + SPRITE 1, 16, 110 + 8, 130, 0 + SPRITE 2, 16 + 16, 110, 132, 0 + SPRITE 3, 16 + 16, 110 + 8, 134, 0 + + ' Hide all unused sprites (4-63) by moving them off-screen to Y=240 + FOR c = 4 TO 63: SPRITE c, 240, 0, 0, 0: NEXT c + + ' Title Logo (BOOM!) + PRINT AT 197, "\200\200\200" + PRINT AT 229, "\200((\200" + PRINT AT 261, "\200\200\200" + PRINT AT 293, "\200((\200" + PRINT AT 325, "\200\200\200" + PRINT AT 202, "(\200\200(" + PRINT AT 234, "\200((\200" + PRINT AT 266, "\200((\200" + PRINT AT 298, "\200((\200" + PRINT AT 330, "(\200\200(" + PRINT AT 207, "(\200\200(" + PRINT AT 239, "\200((\200" + PRINT AT 271, "\200((\200" + PRINT AT 303, "\200((\200" + PRINT AT 335, "(\200\200(" + PRINT AT 212, "\200(((\200" + PRINT AT 244, "\200\200(\200\200" + PRINT AT 276, "\200(\200(\200" + PRINT AT 308, "\200(((\200" + PRINT AT 340, "\200(((\200" + PRINT AT 218, "\200" + PRINT AT 250, "\200" + PRINT AT 282, "\200" + PRINT AT 346, "\200" + + ' -- HIGH SCORE (Row 14, Col 7) -- + PRINT AT 455, "HIGH SCORE: ", d_tthou, d_thou, d_hund, d_tens, d_ones + + ' -- LAST SCORE (Row 15, Col 7) -- + PRINT AT 487, "LAST SCORE: ", d_tthou, d_thou, d_hund, d_tens, d_ones + + ' -- LAST WAVE (Row 16, Col 7) -- + temp_val = last_wave + ' Calculate wave digits manually + r = temp_val / 10 + c = temp_val % 10 + PRINT AT 519, "LAST WAVE: ", 0, 0, 0, r, c + + ' Labels + PRINT AT 647, "SKILL:" + PRINT AT 679, "CONTROLS:" + PRINT AT 711, "PRESS A TO START" + + SCREEN ENABLE ' Turn on the PPU to begin displaying the title screen + input_delay = 0 ' Reset controller delay so menu navigation is instant + + ' -- Title Screen Music Initialization -- + music_step = 0 ' Start sequence at the first note + music_timer = 0 ' Reset timer to trigger the first note immediately + music_pattern = 0 ' Start with Intro (Pattern 0) + music_active = 1 ' Enable the music engine for the title loop + + ' -- Setup Title Bomber (Bomber) -- + x_bomber = 110 ' Center the Bomber horizontally at start + y_bomber = 16 ' Position Bomber at the top of the screen + bomber_dir = 1 ' Set initial movement to the Right + bomber_timer = 0 ' Reset movement timer for random direction changes + + ' -- Setup Title Bomb (Sprite 11) -- + ' This manages the bomb that falls behind the title screen text + found_slot = 0 ' State: 0 = Ready to drop, 1 = Currently falling + temp_val = 0 ' Stores current Y position of the title bomb + r = 0 ' Stores current X position of the title bomb + +title_loop: + WAIT ' Synchronize with the NES Vertical Blank (60 frames per second) + + ' Call the music sequencer every frame if it is currently enabled + IF music_active = 1 THEN GOSUB title_music_update + + ' Increment the random number generator seed while the user is on the title screen + rng_seed = rng_seed + 1 + + ' --- RANDOM BOMBER MOVEMENT (Logo Range: 40 to 199) --- + bomber_timer = bomber_timer + 1 ' Increment the decision timer + + ' Check every 11 frames to see if the Bomber wants to change direction + IF bomber_timer > 10 THEN + bomber_timer = 0 ' Reset the decision timer + c = RANDOM(127) ' Generate a random value between 0 and 127 + IF c > 100 THEN bomber_dir = 1 ' Small chance to force movement Right + IF c < 27 THEN bomber_dir = 0 ' Small chance to force movement Left + END IF + + ' Apply movement based on current direction + IF bomber_dir = 1 THEN + x_bomber = x_bomber + 2 ' Move Right at a constant title-speed + + ' Right Limit: 199 (Strictly above the main logo text) + IF x_bomber > 199 THEN + x_bomber = 199 ' Snap back to the boundary edge + bomber_dir = 0 ' Force direction change to Left + bomber_timer = 0 ' Reset timer to prevent instant jitter + END IF + ELSE + x_bomber = x_bomber - 2 ' Move Left at a constant title-speed + + ' Left Limit: Start of the "B" (40) + IF x_bomber < 40 THEN + x_bomber = 40 ' Snap back to the boundary edge + bomber_dir = 1 ' Force direction change to Right + bomber_timer = 0 ' Reset timer to prevent instant jitter + END IF + END IF + + ' --- TITLE BOMB LOGIC (Decorative Animation) --- + ' Check if the title bomb is currently "reloading" or active + IF found_slot = 0 THEN + ' Bomb is ready: Roll for a random drop chance (~11% chance per frame) + c = RANDOM(255) + IF c > 225 THEN + found_slot = 1 ' Set state to "Falling" + temp_val = y_bomber + 16 ' Set start Y relative to Bomber's height + r = x_bomber + 4 ' Set start X to center under the Bomber + END IF + ELSE + ' Bomb is active: Move it down 2 pixels per frame + temp_val = temp_val + 2 + + ' Update hardware Sprite 11 (Pattern 138, Palette 1) + SPRITE 11, temp_val, r, 138, 1 + + ' Check if bomb reached the "Floor" of the title logo area + IF temp_val >= 88 THEN + found_slot = 0 ' Reset state to "Ready" + temp_val = 0 ' Reset Y position variable + ' Move Sprite 11 off-screen to Y=240 to hide it until next drop + SPRITE 11, 240, 0, 0, 0 + END IF + END IF + + ' --- DRAW THE BOMBER (Sprites 0-3) --- + ' This handles the multi-sprite Bomber and flips the tiles when turning + IF bomber_dir = 1 THEN + ' Moving RIGHT: Use standard tile layout and attributes + SPRITE 0, y_bomber, x_bomber, 128, 0 ' Head Left + SPRITE 1, y_bomber, x_bomber + 8, 130, 0 ' Head Right + SPRITE 2, y_bomber + 16, x_bomber, 132, 0 ' Body Left + SPRITE 3, y_bomber + 16, x_bomber + 8, 134, 0 ' Body Right + ELSE + ' Moving LEFT: Swap tile positions and apply Horizontal Flip (Attribute 64) + SPRITE 0, y_bomber, x_bomber, 130, 64 ' Right tile moves to Left pos, flipped + SPRITE 1, y_bomber, x_bomber + 8, 128, 64 ' Left tile moves to Right pos, flipped + SPRITE 2, y_bomber + 16, x_bomber, 134, 64 ' Right body moves to Left, flipped + SPRITE 3, y_bomber + 16, x_bomber + 8, 132, 64 ' Left body moves to Right, flipped + END IF + + ' --- MENU LOGIC (Selection & Input Debouncing) --- + ' Count down the input delay to prevent ultra-fast scrolling + IF input_delay > 0 THEN + input_delay = input_delay - 1 + END IF + + ' Process new inputs only when the delay timer has reached zero + IF input_delay = 0 THEN + ' NAVIGATE ROWS (Up/Down) + ' Switch between Skill (0) and Control (1) rows + IF cont1.up AND menu_sel > 0 THEN + menu_sel = 0 + input_delay = 12 ' 12-frame pause before next movement + END IF + IF cont1.down AND menu_sel < 1 THEN + menu_sel = 1 + input_delay = 12 ' 12-frame pause before next movement + END IF + + ' CHANGE VALUES (Left/Right) + ' Logic depends on which row the cursor is currently on + IF menu_sel = 0 THEN + ' Modify Difficulty Level (0=Easy, 1=Std, 2=Hard) + IF cont1.left AND difficulty > 0 THEN + difficulty = difficulty - 1 + input_delay = 10 ' Slightly faster repeat for value changes + END IF + IF cont1.right AND difficulty < 2 THEN + difficulty = difficulty + 1 + input_delay = 10 ' Slightly faster repeat for value changes + END IF + ELSE + ' Toggle Control Type (0=Standard, 1=Velocity) + ' This uses binary flipping (1 - current_value) + IF cont1.left OR cont1.right THEN + control_type = 1 - control_type + input_delay = 12 ' Standard pause for toggle + END IF + END IF + END IF + + ' --- REDRAW UI (Text Updates) --- + ' Update the difficulty box text based on current selection + IF difficulty = 0 THEN + PRINT AT 657, "< EASY >" + ELSEIF difficulty = 1 THEN + PRINT AT 657, "< STD >" + ELSE + PRINT AT 657, "< HARD >" + END IF + + ' Update the control mode box text (Standard vs. Velocity) + IF control_type = 0 THEN + PRINT AT 689, "< STD >" + ELSE + PRINT AT 689, "< VEL >" + END IF + + ' --- DRAW CURSOR (Sprite 10) --- + ' Position the circular cursor sprite next to the active menu row + IF menu_sel = 0 THEN + ' Row 0: Skill/Difficulty (Y=159, X=48, Tile 146, Palette 3) + SPRITE 10, 159, 48, 146, 3 + ELSE + ' Row 1: Controls (Y=167, X=48, Tile 146, Palette 3) + SPRITE 10, 167, 48, 146, 3 + END IF + + ' --- START GAME TRIGGER --- + ' Check if the Start/A button is pressed to begin the game + IF cont1.button THEN + ' Use the current title-screen timer to finalize the random seed + c = RANDOM(rng_seed) + + ' Clean up UI elements before transitioning + SPRITE 10, 240, 0, 0, 0 ' Hide the menu cursor off-screen + music_active = 0 ' Stop the title music sequence + GOSUB audio_silence_all ' Immediately kill all active sound registers + + GOTO restart ' Jump to the main game setup + END IF + + GOTO title_loop ' Repeat the title loop until a button is pressed + +' ========================================================================= +' SUBROUTINE: TITLE MUSIC UPDATE +' ========================================================================= +title_music_update: + + ' Note-Off / Staccato Logic + ' Briefly silences channels before a new note to prevent a "slurring" sound + IF music_timer = 2 THEN + POKE $4000, $30 ' Mute Square 1 (Melody) + POKE $4004, $30 ' Mute Square 2 (Harmony) + POKE $4008, $80 ' Mute Triangle (Bass) + ' Noise is skipped here to allow drum decay to finish naturally + END IF + + ' Countdown Timer + ' Stays on the current note until the duration reaches zero + IF music_timer > 0 THEN + music_timer = music_timer - 1 + RETURN ' Exit sub; note is still playing + END IF + + ' FETCH DATA & ADVANCE PATTERNS + ' Determines which musical section we are in and pulls data from arrays + IF music_pattern = 0 THEN + ' --- INTRO (4 Steps) --- + melody_index = title_intro_melody(music_step) + harmony_index = title_intro_harmony(music_step) + bass_index = title_intro_bass(music_step) + drum_index = title_intro_drums(music_step) + duration = title_intro_duration(music_step) + + music_step = music_step + 1 + IF music_step > 3 THEN + music_step = 0 + music_pattern = 1 ' Advance to Main Loop (Pattern A) + END IF + + ELSEIF music_pattern = 1 THEN + ' --- PATTERN A: Main Theme (16 Steps) --- + melody_index = title_melody(music_step) + harmony_index = title_harmony(music_step) + bass_index = title_bass(music_step) + drum_index = title_drums(music_step) + duration = title_duration(music_step) + + music_step = music_step + 1 + IF music_step > 15 THEN + music_step = 0 + music_pattern = 2 ' Advance to Bridge (Pattern B) + END IF + + ELSE + ' --- PATTERN B: Bridge / Variation (16 Steps) --- + melody_index = title_melody_b(music_step) + harmony_index = title_harmony_b(music_step) + bass_index = title_bass_b(music_step) + drum_index = title_drums_b(music_step) + duration = title_duration_b(music_step) + + music_step = music_step + 1 + IF music_step > 15 THEN + music_step = 0 + music_pattern = 1 ' Loop back to Main Theme + END IF + END IF + + ' WRITE TO HARDWARE (POKE TO APU REGISTERS) + + ' Square 1 (Melody) + ' $BF = Duty 10, Length Ctr Disabled, Constant Vol, Vol 15 + IF melody_index = 255 THEN + POKE $4000, $30 ' Silence channel if data is a REST + ELSE + POKE $4000, $BF ' Set volume and duty cycle + POKE $4002, note_table_low(melody_index) ' Fine tune frequency + POKE $4003, note_table_high(melody_index) ' Course tune frequency & trigger + END IF + + ' Square 2 (Harmony) + ' $7A = Duty 01, Length Ctr Disabled, Constant Vol, Vol 10 + IF harmony_index = 255 THEN + POKE $4004, $30 + ELSE + POKE $4004, $7A ' Set volume and different duty for texture + POKE $4006, note_table_low(harmony_index) + POKE $4007, note_table_high(harmony_index) + END IF + + ' Triangle (Bass) + ' $FF = Linear Counter control enabled + IF bass_index = 255 THEN + POKE $4008, $80 + ELSE + POKE $4008, $FF ' Turn Triangle ON + POKE $400A, note_table_low(bass_index) + POKE $400B, note_table_high(bass_index) + END IF + + ' Noise (Drums) + ' Uses Hardware Envelopes ($1x) for percussion decay + IF drum_index = 1 THEN + ' Snare Drum: Mid-pitch hiss with decay + POKE $400C, $18 + POKE $400E, $01 + ELSEIF drum_index = 2 THEN + ' Kick Drum: Low-frequency thud with decay + POKE $400C, $1A + POKE $400E, $0F + ELSE + ' Silence Noise Channel + POKE $400C, $30 + END IF + + ' Start Note Timer + ' Set duration for the current note before next update cycle + music_timer = duration + + RETURN + +' ========================================================================= +' GAME SETUP +' ========================================================================= +restart: + + POKE $4015, $0F ' enable all APU channels (including noise) + + ' -- GAME PALETTES (Reset for Gameplay) -- + ' Background Palettes (Used by Cityscape and Sky) + PALETTE 0, $0F ' Slot 0 (Black - Global Background) + PALETTE 1, $1A ' Slot 1 (Green - Floor/Grass) + PALETTE 2, $01 ' Slot 2 (Medium Blue - Sky/Buildings) + PALETTE 3, $00 ' Slot 3 (Dark Grey - Buildings) + + ' Sprite Palette 0 (Bomber / Bomber) + PALETTE 16, $0F ' Slot 0 (Transparent) + PALETTE 17, $0F ' Slot 1 (Black - Outline) + PALETTE 18, $30 ' Slot 2 (White - Face/Eyes) + PALETTE 19, $27 ' Slot 3 (Orange - Hat/Clothes) + + ' Sprite Palette 1 (Bombs) + PALETTE 20, $0F ' Slot 0 (Transparent) + PALETTE 21, $0F ' Slot 1 (Black - Fuse/Wick) + PALETTE 22, $00 ' Slot 2 (Grey - Highlight) + PALETTE 23, $30 ' Slot 3 (White - Spark) + + ' Sprite Palette 2 (Score Digits) + PALETTE 24, $0F ' Slot 0 (Transparent) + PALETTE 25, $0F ' Slot 1 (Black - Drop Shadow) + PALETTE 26, $0F ' Slot 2 (Black - Unused) + PALETTE 27, $27 ' Slot 3 (Orange - Digit Color) + + ' Sprite Palette 3 (Buckets & Splash) + PALETTE 28, $0F ' Slot 0 (Transparent) + PALETTE 29, $0F ' Slot 1 (Black - Outline) + PALETTE 30, $12 ' Slot 2 (Blue - Water/Splash) + PALETTE 31, $07 ' Slot 3 (Dark Brown - Bucket Body) + + ' -- Reset Gameplay Variables -- + score = 0 ' Clear current session score + d_tthou = 0 ' Reset Ten-Thousands digit display + d_thou = 0 ' Reset Thousands digit display + d_hund = 0 ' Reset Hundreds digit display + d_tens = 0 ' Reset Tens digit display + d_ones = 0 ' Reset Ones digit display + lives = 3 ' Start player with a full stack of 3 buckets + wave = 1 ' Begin the challenge at Wave 1 + + ' -- Position Actors -- + x_bucket = 110 ' Start player buckets in the center of the screen + x_bomber = 110 ' Start the Bomber in the center of the sky + y_bomber = 26 ' Position the Bomber just below the cityscape skyline + bomber_dir = 1 ' Set initial Bomber movement to the Right + + ' -- Global Sprite Reset -- + ' Clear all 64 sprites by moving them to Y=255 (Off-screen) + FOR c = 0 TO 63 + SPRITE c, 255, 0, 0, 0 + NEXT c + + ' -- Difficulty Scaling Update -- + ' Adjust collision math and movement boundaries based on Title Screen selection + IF difficulty = 0 THEN + ' EASY: Wide catch area (4 sprites / 32 pixels) + hit_width = 32 + right_limit = 216 ' Clamp X earlier to account for wider sprites + ELSEIF difficulty = 1 THEN + ' STD: Medium catch area (3 sprites / 24 pixels) + hit_width = 24 + right_limit = 224 ' Standard screen clamping + ELSE + ' HARD: Narrow catch area (2 sprites / 16 pixels) + hit_width = 16 + right_limit = 232 ' Allow further movement to the right + END IF + + ' -- Reset Splash Animation State -- + ' Clear all active water splash timers and offsets across all 3 bucket rows + FOR r = 0 TO 2 + splash_t(r) = 0 ' Reset frame timer (kills active splashes) + splash_x(r) = 0 ' Reset horizontal offset + splash_y(r) = 0 ' Reset vertical offset + NEXT r + + ' -- Prepare Display for Game World -- + SCREEN DISABLE ' Turn off PPU to allow fast background tile writing + CLS ' Clear the current Name Table (background) + + ' Clean OAM (Sprite Memory) by hiding all 64 sprites off-screen + FOR c = 0 TO 63 + SPRITE c, 240, 0, 0, 0 + NEXT c + + ' --- DRAW STATIC CITYSCAPE --- + ' Draw the sky/clouds (Pattern 42 '*') across the top 2 rows + FOR r = 0 TO 1 + PRINT AT r * 32, "***********************(((******" + NEXT r + + ' Draw the building tops and skyline details (Rows 2-6) + PRINT AT 2 * 32, "****(((((*(((*((((*****( (*(((((" + PRINT AT 3 * 32, "****( ( (*( (*( ((*****(((*( ( (" + PRINT AT 4 * 32, "(((*(((((*(((*((((*(((*(((*(((((" + PRINT AT 5 * 32, "( (*( ( (*( (*((((*(((*(((*( (((" + PRINT AT 6 * 32, "(((*(((((*(((*((((*(((*(((*(((((" + + ' Draw the main city wall/floor (Pattern 41 ')') from Row 7 down to 27 + FOR r = 7 TO 27 + PRINT AT r * 32, "))))))))))))))))))))))))))))))))" + NEXT r + + SCREEN ENABLE ' Turn display back on now that background is ready + + ' -- Final Game State Initialization -- + ' Ensure all 8 bomb slots are marked as inactive (0) before the wave starts + FOR c = 0 TO 7 + bomb_s(c) = 0 + NEXT c + + GOSUB new_game_pause ' Show "Ready?" prompt and wait for player input + GOTO start_wave ' Jump to the wave-specific logic and bomb spawning + +' ========================================================================= +' WAVE SETUP (Difficulty & Scaling Logic) +' ========================================================================= +start_wave: + spawn_timer = 30 ' Initial 0.5-second pause before the first bomb drops + + ' -- BOMB QUANTITY -- + ' Gradually increase total bombs per wave (e.g., Wave 1 = 17, Wave 10 = 35) + bombs_remaining = 15 + (wave * 2) + + ' -- VERTICAL FALL SPEED -- + bomb_speed = 2 ' Base speed for Standard/Easy early waves + IF difficulty = 2 THEN bomb_speed = 3 ' Hard mode starts at a higher base speed + IF wave > 2 THEN bomb_speed = 3 ' Increase speed at Wave 3 + IF wave > 5 THEN bomb_speed = 4 ' Increase speed at Wave 6 + IF wave > 8 THEN bomb_speed = 5 ' Increase speed at Wave 9 + + ' Fairness Speed Cap: Ensure bombs never exceed 5 pixels per frame + IF bomb_speed > 5 THEN bomb_speed = 5 + + ' -- SPAWN DENSITY (The "Rhythm") -- + ' Formula creates a tighter drop interval as waves progress + max_spawn_delay = 20 - (wave * 2) + + ' Adjust spawn rhythm based on chosen skill level + IF difficulty = 0 THEN max_spawn_delay = max_spawn_delay + 4 ' Slower drops for Easy + IF difficulty = 2 THEN max_spawn_delay = max_spawn_delay - 4 ' Faster drops for Hard + + ' fairness Floor: Never drop faster than once every 4 frames (the limit of human reaction) + IF max_spawn_delay < 4 THEN max_spawn_delay = 4 + + WAIT ' Final frame sync before jumping into the game loop + +' ========================================================================= +' MAIN GAME LOOP +' ========================================================================= +game_loop: + + ' --- MOVEMENT SELECTION (Player Input & Physics) --- + + IF control_type = 0 THEN + ' -- TIGHT CONTROLS (Classic Digital Style) -- + ' Instant 4-pixel movement with boundary checking + IF cont1.left AND x_bucket > 8 THEN + x_bucket = x_bucket - 4 + END IF + IF cont1.right AND x_bucket < right_limit THEN + x_bucket = x_bucket + 4 + END IF + ELSE + ' -- SMOOTH CONTROLS (Momentum Physics + Magnet Assist) -- + + ' Acceleration Logic + IF cont1.left THEN + b_dir = 1 ' Set intent to Left + b_subpixel = b_subpixel + 80 + ' If subpixel overflows (8-bit roll), increment actual speed + IF b_subpixel < 80 THEN + IF b_speed < 4 THEN b_speed = b_speed + 1 + END IF + ELSEIF cont1.right THEN + b_dir = 2 ' Set intent to Right + b_subpixel = b_subpixel + 80 + IF b_subpixel < 80 THEN + IF b_speed < 4 THEN b_speed = b_speed + 1 + END IF + ELSE + ' 2. Friction/Deceleration (Active when no buttons are pressed) + b_subpixel = b_subpixel + 40 + IF b_subpixel < 40 THEN + IF b_speed > 0 THEN + b_speed = b_speed - 1 + ELSE + b_dir = 0 ' Full stop + END IF + END IF + END IF + + ' Apply Velocity to Screen Position + IF b_dir = 1 THEN x_bucket = x_bucket - b_speed + IF b_dir = 2 THEN x_bucket = x_bucket + b_speed + + ' Magnet Assist (Corrects for near-misses in Smooth mode) + ' Loops through all active bombs to see if any are close enough to "pull" the bucket + FOR c = 0 TO 7 + IF bomb_s(c) = 1 THEN + ' Manual Absolute Difference calculation (8-bit safe) + temp_val = bomb_x(c) - x_bucket + IF temp_val > 127 THEN temp_val = 256 - temp_val + + ' If bucket is within 12 pixels of a falling bomb, apply a 1-pixel nudge + IF temp_val < 12 THEN + IF b_dir = 2 AND bomb_x(c) > x_bucket THEN x_bucket = x_bucket + 1 + IF b_dir = 1 AND bomb_x(c) < x_bucket THEN x_bucket = x_bucket - 1 + END IF + END IF + NEXT c + END IF + + ' -- Global Boundary Clamping -- + ' Prevents the bucket from sliding off the left or right edges of the city + IF x_bucket < 8 THEN + x_bucket = 8 ' Snap to left boundary + b_speed = 0 ' Kill momentum in Velocity mode + END IF + IF x_bucket > right_limit THEN + x_bucket = right_limit ' Snap to right boundary (calculated by difficulty) + b_speed = 0 ' Kill momentum in Velocity mode + END IF + + ' -- Bomber (Bomber) AI & Movement -- + ' Decision Logic: Every 11 frames, roll for a possible direction change + bomber_timer = bomber_timer + 1 + IF bomber_timer > 10 THEN + bomber_timer = 0 + c = RANDOM(127) + IF c > 100 THEN bomber_dir = 1 ' Shift intent to Right + IF c < 27 THEN bomber_dir = 0 ' Shift intent to Left + END IF + + ' Physical Movement & Screen Wrapping + IF bomber_dir = 1 THEN + x_bomber = x_bomber + 2 ' Standard horizontal speed + IF x_bomber > 220 THEN bomber_dir = 0 ' Turn around at right edge + ELSE + x_bomber = x_bomber - 2 + IF x_bomber < 16 THEN bomber_dir = 1 ' Turn around at left edge + END IF + + ' -- Bomb Spawning Logic -- + active_bombs = 0 ' Reset counter for the frame's update + + ' Only decrement spawn timer if there are bombs left in the wave + IF spawn_timer > 0 THEN spawn_timer = spawn_timer - 1 + + ' Trigger a new drop when timer hits zero and wave is not empty + IF spawn_timer = 0 AND bombs_remaining > 0 THEN + ' Find an empty slot in the bomb array (Indices 0-7) + FOR c = 0 TO 7 + IF bomb_s(c) = 0 THEN + bomb_x(c) = x_bomber + 4 ' Spawn at Bomber's current X center + bomb_y(c) = y_bomber + 24 ' Spawn just below the Bomber's sprites + bomb_s(c) = 1 ' Activate the bomb + spawn_timer = max_spawn_delay ' Reset rhythm timer + bombs_remaining = bombs_remaining - 1 ' Deduct from wave pool + c = 7 ' Exit the search early (one bomb per frame) + END IF + NEXT c + END IF + + FOR c = 0 TO 7 + IF bomb_s(c) = 1 THEN + active_bombs = active_bombs + 1 + bomb_y(c) = bomb_y(c) + bomb_speed + hit_detected = 0 + + ' Catch zones + IF bomb_x(c) > x_bucket - 4 AND bomb_x(c) < x_bucket + hit_width THEN + ' Bottom bucket row: centered at 212, 8px tall + IF bomb_y(c) >= 212 AND bomb_y(c) < 220 THEN hit_detected = 1 + + ' Middle bucket row (only if 2+ lives): centered at 194 + IF lives >= 2 THEN + IF bomb_y(c) >= 194 AND bomb_y(c) < 202 THEN hit_detected = 1 + END IF + + ' Top bucket row (only if 3 lives): centered at 176 + IF lives >= 3 THEN + IF bomb_y(c) >= 176 AND bomb_y(c) < 184 THEN hit_detected = 1 + END IF + END IF + + IF hit_detected = 1 THEN + bomb_s(c) = 0 + GOSUB splash_sfx + IF score < 9999 THEN score = score + 1 + GOSUB add_score_point + + ' --- SPLASH TRIGGER --- + ' Determine which bucket row was hit (matching the ranges above) + IF bomb_y(c) >= 212 AND bomb_y(c) < 220 THEN r = 0 + IF bomb_y(c) >= 194 AND bomb_y(c) < 202 THEN r = 1 + IF bomb_y(c) >= 176 AND bomb_y(c) < 184 THEN r = 2 + + splash_x(r) = bomb_x(c) - x_bucket + splash_y(0) = 212 - 8 + splash_y(1) = 194 - 8 + splash_y(2) = 176 - 8 + splash_t(r) = 12 ' lasts 12 frames + + + END IF + + IF bomb_y(c) >= 230 THEN + bomb_s(c) = 0 + GOSUB bomb_missed_sfx + lives = lives - 1 + IF lives < 1 THEN + lives = 0 + + ' Clear splash before game over + FOR r = 0 TO 2 + splash_t(r) = 0 + SPRITE 4 + r, 255, 0, 0, 0 + NEXT r + GOSUB game_over_sfx + GOTO game_over_screen + END IF + WAIT + + ' Clear splash before life-lost screen + FOR r = 0 TO 2 + splash_t(r) = 0 + SPRITE 4 + r, 255, 0, 0, 0 + NEXT r + + GOTO life_lost_draw + END IF + END IF + NEXT c + + ' If wave is done, clear splash and go to wave complete + IF bombs_remaining = 0 AND active_bombs = 0 THEN + GOSUB wave_complete_sfx + FOR r = 0 TO 2 + splash_t(r) = 0 + SPRITE 4 + r, 255, 0, 0, 0 + NEXT r + GOTO wave_completed + END IF + + ' --- DRAWING --- + FOR c = 20 TO 39 : SPRITE c, 255, 0, 0, 0 : NEXT c + FOR c = 48 TO 63 : SPRITE c, 255, 0, 0, 0 : NEXT c + + GOSUB draw_bomber_sub + + ' -- DETERMINE PRIORITY ROTATION -- + ' We rotate which row gets the "low" (high priority) sprite indices + ' using the rng_seed as a frame counter. + ' This was designed to help with flicker, but honestly doesn't really help much. + temp_val = rng_seed % 3 + IF temp_val = 0 THEN + s_row1 = 24 + s_row2 = 27 + s_row3 = 30 + s_ex1 = 33 + s_ex2 = 34 + s_ex3 = 35 + ELSEIF temp_val = 1 THEN + s_row1 = 30 + s_row2 = 24 + s_row3 = 27 + s_ex1 = 35 + s_ex2 = 33 + s_ex3 = 34 + ELSE + s_row1 = 27 + s_row2 = 30 + s_row3 = 24 + s_ex1 = 34 + s_ex2 = 35 + s_ex3 = 33 + END IF + + ' -- DRAW BUCKET ROWS -- + col1_x = x_bucket + col2_x = x_bucket + 8 + col3_x = x_bucket + 16 + col4_x = x_bucket + 24 + + b_vis_1 = 212 + IF lives >= 2 THEN + b_vis_2 = 194 + ELSE + b_vis_2 = 255 + END IF + IF lives >= 3 THEN + b_vis_3 = 176 + ELSE + b_vis_3 = 255 + END IF + + ' Draw Row 1 + SPRITE s_row1, b_vis_1, col1_x, 136, 3 + SPRITE s_row1 + 1, b_vis_1, col2_x, 136, 3 + IF difficulty < 2 THEN + SPRITE s_row1 + 2, b_vis_1, col3_x, 136, 3 + ELSE + SPRITE s_row1 + 2, 255, 0, 0, 0 + END IF + IF difficulty = 0 THEN + SPRITE s_ex1, b_vis_1, col4_x, 136, 3 + ELSE + SPRITE s_ex1, 255, 0, 0, 0 + END IF + + ' Draw Row 2 + SPRITE s_row2, b_vis_2, col1_x, 136, 3 + SPRITE s_row2 + 1, b_vis_2, col2_x, 136, 3 + IF difficulty < 2 AND b_vis_2 < 255 THEN + SPRITE s_row2 + 2, b_vis_2, col3_x, 136, 3 + ELSE + SPRITE s_row2 + 2, 255, 0, 0, 0 + END IF + IF difficulty = 0 AND b_vis_2 < 255 THEN + SPRITE s_ex2, b_vis_2, col4_x, 136, 3 + ELSE + SPRITE s_ex2, 255, 0, 0, 0 + END IF + + ' Draw Row 3 + SPRITE s_row3, b_vis_3, col1_x, 136, 3 + SPRITE s_row3 + 1, b_vis_3, col2_x, 136, 3 + IF difficulty < 2 AND b_vis_3 < 255 THEN + SPRITE s_row3 + 2, b_vis_3, col3_x, 136, 3 + ELSE + SPRITE s_row3 + 2, 255, 0, 0, 0 + END IF + IF difficulty = 0 AND b_vis_3 < 255 THEN + SPRITE s_ex3, b_vis_3, col4_x, 136, 3 + ELSE + SPRITE s_ex3, 255, 0, 0, 0 + END IF + + ' Splash animation follows bucket + FOR r = 0 TO 2 + IF splash_t(r) > 0 THEN + SPRITE 4 + r, splash_y(r), x_bucket + splash_x(r), 140, 3 + splash_t(r) = splash_t(r) - 1 + ELSE + SPRITE 4 + r, 255, 0, 0, 0 + END IF + NEXT r + + ' Draw Score + GOSUB redraw_score + + FOR c = 0 TO 7 + IF bomb_s(c) = 1 THEN + SPRITE 40 + c, bomb_y(c), bomb_x(c), 138, 1 + ELSE + SPRITE 40 + c, 255, 0, 0, 0 + END IF + NEXT c + + WAIT + + GOTO game_loop + +' ========================================================================= +' MUSIC DATA: NOTE TABLE +' ========================================================================= + +note_table_low: + DATA $F1, $D5, $BC, $A8, $8F, $7A, $68, $5A, $4E, $45, $3D, $36 + +note_table_high: + DATA $02, $02, $02, $02, $03, $03, $03, $03, $03, $03, $03, $03 + +' ========================================================================= +' MUSIC DATA: TITLE THEME SEQUENCES +' ========================================================================= + +title_intro_melody: + DATA 7, 9, 12, 11 + +title_intro_harmony: + DATA 4, 7, 9, 7 + +title_intro_bass: + DATA 0, 0, 0, 0 + +title_intro_drums: + DATA 2,1,2,1 + +title_intro_duration: + DATA 18,18,18,18 + +title_melody: + DATA 0,2,4,5,7,5,4,2,0,2,4,7,5,4,2,0 + +title_harmony: + DATA 7,9,11,0,2,0,11,9,7,9,11,2,0,11,9,7 + +title_bass: + DATA 0,0,7,7,0,0,7,7,0,0,7,7,0,0,7,7 + +title_drums: + DATA 2,0,1,0,2,0,1,0,2,0,1,0,2,1,2,1 + +title_duration: + DATA 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 + +title_melody_b: + DATA 4,5,7,9,11,9,7,5,7,9,11,12,11,9,7,5 + +title_harmony_b: + DATA 7,9,11,12,12,11,9,7,9,11,12,14,12,11,9,7 + +title_bass_b: + DATA 0,0,5,5,7,7,9,9,5,5,7,7,9,9,11,11 + +title_drums_b: + DATA 2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1 + +title_duration_b: + DATA 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14 + +audio_silence_all: + ' Square 1 + POKE $4000, $30 + POKE $4001, $00 + POKE $4002, $00 + POKE $4003, $00 + + ' Square 2 + POKE $4004, $30 + POKE $4005, $00 + POKE $4006, $00 + POKE $4007, $00 + + ' Triangle + POKE $4008, $80 + POKE $400A, $00 + POKE $400B, $00 + + ' Noise + POKE $400C, $00 + POKE $400E, $00 + POKE $400F, $00 + + RETURN + +splash_sfx: + ' Ensure noise channel is enabled + POKE $4015, $0F + + ' --- Start of splash --- + ' Volume = 0x1F (loud), constant envelope + POKE $400C, $1F + + ' Start with a mid‑pitch noise (watery) + POKE $400E, $84 + + ' Trigger the noise channel (critical) + POKE $400F, $FF + + ' Hold the initial burst for a frame + WAIT + + ' Slightly lower pitch (falling splash) + POKE $400E, $06 + WAIT + + ' Lower again (tail of splash) + POKE $400E, $08 + WAIT + + ' Fade out + POKE $400C, $0C + WAIT + + ' Silence + POKE $400C, $00 + RETURN + +bomb_missed_sfx: + ' Enable channels + POKE $4015, $0F + + ' --- Initial blast --- + POKE $400C, $1F ' max volume + POKE $400E, $80 ' short-mode noise, high pitch + POKE $400F, $20 ' trigger with a longer length + WAIT + WAIT + + ' --- Mid burst --- + POKE $400E, $88 + WAIT + WAIT + + ' --- Tail rumble --- + POKE $400E, $8F + WAIT + WAIT + + ' --- Fade --- + POKE $400C, $0C + WAIT + WAIT + + ' Silence + POKE $400C, $00 + RETURN + +wave_complete_sfx: + ' Enable channels + POKE $4015, $0F + + ' First note (high) + POKE $4000, $BF ' square 1, constant volume + POKE $4002, $4E ' B5 low byte + POKE $4003, $03 ' B5 high byte + WAIT + WAIT + + ' Second note (higher) + POKE $4002, $45 ' C6 low byte + POKE $4003, $03 ' C6 high byte + WAIT + WAIT + + ' Silence + POKE $4000, $30 + RETURN + +game_over_sfx: + ' Enable channels + POKE $4015, $0F + + ' Note 1 (mid) + POKE $4000, $BF + POKE $4002, $A8 ' D5-ish + POKE $4003, $02 + WAIT + WAIT + + ' Note 2 (lower) + POKE $4002, $BC ' C#5-ish + POKE $4003, $02 + WAIT + WAIT + + ' Note 3 (lowest) + POKE $4002, $D5 ' B4-ish + POKE $4003, $02 + WAIT + WAIT + + ' Silence + POKE $4000, $30 + RETURN + +' --------------------------------------------------------- +' SUBROUTINE: ADD SCORE POINT +' --------------------------------------------------------- +add_score_point: + ' Increment total score tracker (for high score comparison later) + IF score_val < 32767 THEN + score_val = score_val + 1 + END IF + + ' Ripple Carry Logic for 5 Digits + d_ones = d_ones + 1 + IF d_ones > 9 THEN + d_ones = 0 + d_tens = d_tens + 1 + IF d_tens > 9 THEN + d_tens = 0 + d_hund = d_hund + 1 + IF d_hund > 9 THEN + d_hund = 0 + d_thou = d_thou + 1 + ' Extra Life every 1,000 points + IF lives < 3 THEN + lives = lives + 1 + END IF + IF d_thou > 9 THEN + d_thou = 0 + d_tthou = d_tthou + 1 + IF d_tthou > 9 THEN + ' Hard Cap at 99,999 + d_tthou = 9 + d_thou = 9 + d_hund = 9 + d_tens = 9 + d_ones = 9 + END IF + END IF + END IF + END IF + END IF + RETURN + +' --------------------------------------------------------- +' SUBROUTINE: DRAW BOMBER +' --------------------------------------------------------- +draw_bomber_sub: + IF bomber_dir = 1 THEN + bomber_attr = 0 + SPRITE 0, y_bomber, x_bomber, 128, bomber_attr + SPRITE 1, y_bomber, x_bomber + 8, 130, bomber_attr + SPRITE 2, y_bomber + 16, x_bomber, 132, bomber_attr + SPRITE 3, y_bomber + 16, x_bomber + 8, 134, bomber_attr + ELSE + bomber_attr = 64 + SPRITE 0, y_bomber, x_bomber, 130, bomber_attr + SPRITE 1, y_bomber, x_bomber + 8, 128, bomber_attr + SPRITE 2, y_bomber + 16, x_bomber, 134, bomber_attr + SPRITE 3, y_bomber + 16, x_bomber + 8, 132, bomber_attr + END IF + RETURN + +' --------------------------------------------------------- +' SUBROUTINE: WAVE COMPLETED +' --------------------------------------------------------- +wave_completed: + FOR r = 0 TO 2 + splash_t(r) = 0 + SPRITE 4 + r, 255, 0, 0, 0 + NEXT r + FOR c = 0 TO 7 + bomb_s(c) = 0 + SPRITE 40 + c, 255, 0, 0, 0 + NEXT c + GOSUB draw_bomber_sub + + ' Display using the wave variable directly + PRINT AT 898, "WAVE " + PRINT AT 903, wave + PRINT AT 905, " COMPLETE! - PRESS A" + + WAIT + + WHILE 1 + WAIT + IF cont1.button THEN + PRINT AT 896, " " + GOSUB redraw_score + wave = wave + 1 + GOTO start_wave + END IF + WEND + +' --------------------------------------------------------- +' SUBROUTINE: LIFE LOST +' --------------------------------------------------------- +life_lost_draw: + FOR c = 0 TO 7 + IF bomb_s(c) = 1 THEN + SPRITE 40 + c, bomb_y(c), bomb_x(c), 138, 1 + ELSE + SPRITE 40 + c, 255, 0, 0, 0 + END IF + NEXT c + GOSUB draw_bomber_sub + + temp_val = rng_seed % 3 + IF temp_val = 0 THEN + s_row1 = 24 + s_row2 = 27 + s_row3 = 30 + s_ex1 = 33 + s_ex2 = 34 + s_ex3 = 35 + ELSEIF temp_val = 1 THEN + s_row1 = 30 + s_row2 = 24 + s_row3 = 27 + s_ex1 = 35 + s_ex2 = 33 + s_ex3 = 34 + ELSE + s_row1 = 27 + s_row2 = 30 + s_row3 = 24 + s_ex1 = 34 + s_ex2 = 35 + s_ex3 = 33 + END IF + + col1_x = x_bucket + col2_x = x_bucket + 8 + col3_x = x_bucket + 16 + col4_x = x_bucket + 24 + + b_vis_1 = 212 + IF lives >= 2 THEN + b_vis_2 = 194 + ELSE + b_vis_2 = 255 + END IF + IF lives >= 3 THEN + b_vis_3 = 176 + ELSE + b_vis_3 = 255 + END IF + + ' Draw Row 1 + SPRITE s_row1, b_vis_1, col1_x, 136, 3 + SPRITE s_row1 + 1, b_vis_1, col2_x, 136, 3 + IF difficulty < 2 THEN + SPRITE s_row1 + 2, b_vis_1, col3_x, 136, 3 + ELSE + SPRITE s_row1 + 2, 255, 0, 0, 0 + END IF + IF difficulty = 0 THEN + SPRITE s_ex1, b_vis_1, col4_x, 136, 3 + ELSE + SPRITE s_ex1, 255, 0, 0, 0 + END IF + + ' Draw Row 2 + SPRITE s_row2, b_vis_2, col1_x, 136, 3 + SPRITE s_row2 + 1, b_vis_2, col2_x, 136, 3 + IF difficulty < 2 AND b_vis_2 < 255 THEN + SPRITE s_row2 + 2, b_vis_2, col3_x, 136, 3 + ELSE + SPRITE s_row2 + 2, 255, 0, 0, 0 + END IF + IF difficulty = 0 AND b_vis_2 < 255 THEN + SPRITE s_ex2, b_vis_2, col4_x, 136, 3 + ELSE + SPRITE s_ex2, 255, 0, 0, 0 + END IF + + ' Draw Row 3 + SPRITE s_row3, b_vis_3, col1_x, 136, 3 + SPRITE s_row3 + 1, b_vis_3, col2_x, 136, 3 + IF difficulty < 2 AND b_vis_3 < 255 THEN + SPRITE s_row3 + 2, b_vis_3, col3_x, 136, 3 + ELSE + SPRITE s_row3 + 2, 255, 0, 0, 0 + END IF + IF difficulty = 0 AND b_vis_3 < 255 THEN + SPRITE s_ex3, b_vis_3, col4_x, 136, 3 + ELSE + SPRITE s_ex3, 255, 0, 0, 0 + END IF + + GOTO life_lost_pause + +' --------------------------------------------------------- +' SUBROUTINE: LIFE LOST PAUSE +' --------------------------------------------------------- +life_lost_pause: + FOR c = 0 TO 7 + bomb_s(c) = 0 + SPRITE 40 + c, 255, 0, 0, 0 + NEXT c + FOR r = 0 TO 2 + splash_t(r) = 0 + SPRITE 4 + r, 255, 0, 0, 0 + NEXT r + GOSUB draw_bomber_sub + + ' UI on Row 28 + PRINT AT 901, "BOMB MISSED! - PRESS A" + + WHILE 1 + WAIT + IF cont1.button THEN + PRINT AT 896, " " + GOSUB redraw_score + GOTO start_wave + END IF + WEND + +' --------------------------------------------------------- +' SUBROUTINE: NEW GAME PAUSE +' --------------------------------------------------------- +new_game_pause: + FOR r = 0 TO 2 + splash_t(r) = 0 + SPRITE 4 + r, 255, 0, 0, 0 + NEXT r + FOR c = 0 TO 7 + bomb_s(c) = 0 + SPRITE 40 + c, 240, 0, 0, 0 + NEXT c + GOSUB draw_bomber_sub + + ' Print to Row 28 + PRINT AT 900, "READY? PRESS A TO START" + + WHILE 1 + WAIT + IF cont1.button THEN + ' Clear Row 28 + PRINT AT 896, " " + GOSUB redraw_score + RETURN + END IF + WEND + +' --------------------------------------------------------- +' SUBROUTINE: GAME OVER +' --------------------------------------------------------- +game_over_screen: + FOR c = 0 TO 63 + SPRITE c, 255, 0, 0, 0 + NEXT c + + PRINT AT 896, " " + GOSUB redraw_score + PRINT AT 907, "GAME OVER" + + ' Save the current wave + last_wave = wave + + ' Manual Digit-by-Digit High Score Comparison + ' (We only update high score if current score is higher) + r = 0 ' Flag for "is current score higher?" + IF d_tthou > h_tthou THEN r = 1 + IF d_tthou = h_tthou AND d_thou > h_thou THEN r = 1 + IF d_tthou = h_tthou AND d_thou = h_thou AND d_hund > h_hund THEN r = 1 + IF d_tthou = h_tthou AND d_thou = h_thou AND d_hund = h_hund AND d_tens > h_tens THEN r = 1 + IF d_tthou = h_tthou AND d_thou = h_thou AND d_hund = h_hund AND d_tens = h_tens AND d_ones > h_ones THEN r = 1 + + IF r = 1 THEN + ' Update High Score Digit Variables + h_tthou = d_tthou + h_thou = d_thou + h_hund = d_hund + h_tens = d_tens + h_ones = d_ones + END IF + + WHILE 1 + WAIT + IF cont1.button THEN + GOTO title_screen + END IF + WEND + +' --------------------------------------------------------- +' SUBROUTINE: REFRESH GAME SCREEN +' Wipes everything and redraws the clean cityscape +' --------------------------------------------------------- +refresh_game_screen: + SCREEN DISABLE + CLS + ' Redraw the Sky and Top of Buildings + FOR r = 0 TO 1 + PRINT AT r * 32, "***********************(((******" + NEXT r + PRINT AT 64, "****(((((*(((*((((*****( (*(((((" + PRINT AT 96, "****( ( (*( (*( ((*****(((*( ( (" + PRINT AT 128, "(((*(((((*(((*((((*(((*(((*(((((" + PRINT AT 160, "( (*( ( (*( (*((((*(((*(((*( (((" + PRINT AT 192, "(((*(((((*(((*((((*(((*(((*(((((" + + ' Redraw the Main Building Area (The green area) + FOR r = 7 TO 27 + PRINT AT r * 32, "))))))))))))))))))))))))))))))))" + NEXT r + + SCREEN ENABLE + RETURN + +redraw_score: + ' All digits on Row 1 (Y=8) + ' Ten Thousands + SPRITE 15, 8, 208, 160 + (d_tthou * 2), 2 + ' Thousands + SPRITE 16, 8, 216, 160 + (d_thou * 2), 2 + ' Hundreds + SPRITE 17, 8, 224, 160 + (d_hund * 2), 2 + ' Tens + SPRITE 18, 8, 232, 160 + (d_tens * 2), 2 + ' Ones + SPRITE 19, 8, 240, 160 + (d_ones * 2), 2 + RETURN + +' -- GRAPHICS -- + CHRROM 0 + +' == BACKGROUND NUMBERS (48+) == + CHRROM PATTERN 48 + ' 0 + BITMAP ".33333.." + BITMAP "33...33." + BITMAP "33...33." + BITMAP "33...33." + BITMAP "33...33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "........" + ' 1 + BITMAP "...33..." + BITMAP "..333..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP ".333333." + BITMAP "........" + ' 2 + BITMAP ".33333.." + BITMAP "33...33." + BITMAP ".....33." + BITMAP "...33..." + BITMAP "..33...." + BITMAP "33......" + BITMAP "3333333." + BITMAP "........" + ' 3 + BITMAP "333333.." + BITMAP "....33.." + BITMAP "...33..." + BITMAP "....33.." + BITMAP ".....33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "........" + ' 4 + BITMAP "...333.." + BITMAP "..333..." + BITMAP ".33.3..." + BITMAP "33..3..." + BITMAP "3333333." + BITMAP "....3..." + BITMAP "....3..." + BITMAP "........" + ' 5 + BITMAP "3333333." + BITMAP "33......" + BITMAP "333333.." + BITMAP ".....33." + BITMAP ".....33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "........" + ' 6 + BITMAP ".33333.." + BITMAP "33......" + BITMAP "333333.." + BITMAP "33...33." + BITMAP "33...33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "........" + ' 7 + BITMAP "3333333." + BITMAP "33...33." + BITMAP "...33..." + BITMAP "..33...." + BITMAP "..33...." + BITMAP "..33...." + BITMAP "..33...." + BITMAP "........" + ' 8 + BITMAP ".33333.." + BITMAP "33...33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "33...33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "........" + ' 9 + BITMAP ".33333.." + BITMAP "33...33." + BITMAP ".333333." + BITMAP ".....33." + BITMAP "....33.." + BITMAP "...33..." + BITMAP ".333...." + BITMAP "........" + + ' Pattern 58: COLON (Grey on Black) + BITMAP "........" + BITMAP "..33...." + BITMAP "..33...." + BITMAP "........" + BITMAP "..33...." + BITMAP "..33...." + BITMAP "........" + BITMAP "........" + + ' PATTERN 127: COPYRIGHT SYMBOL (Uses 3 = Grey) + CHRROM PATTERN 127 + BITMAP ".33333.." + BITMAP "3.....3." + BITMAP "3.333.3." + BITMAP "3.3...3." + BITMAP "3.3...3." + BITMAP "3.333.3." + BITMAP "3.....3." + BITMAP ".33333.." + + ' PATTERN 200: THE TITLE LOGO BLOCK (Uses 1 = ORANGE) + CHRROM PATTERN 200 + BITMAP "11111111" + BITMAP "1......1" + BITMAP "1.1..1.1" + BITMAP "1..11..1" + BITMAP "1..11..1" + BITMAP "1.1..1.1" + BITMAP "1......1" + BITMAP "11111111" + + ' PATTERN 201: SOLID ORANGE BLOCK (Uses 1 = ORANGE) + CHRROM PATTERN 201 + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + ' == SPRITES (128+) == + CHRROM PATTERN 128 + + ' Bomber + BITMAP "....1111" + BITMAP "...11111" + BITMAP "...11111" + BITMAP "...11111" + BITMAP "11111..1" + BITMAP "11111111" + BITMAP "33333333" + BITMAP "....333." + + BITMAP "....333." + BITMAP "....3333" + BITMAP "....333." + BITMAP "....3..3" + BITMAP ".....333" + BITMAP ".....333" + BITMAP ".....111" + BITMAP "...22222" + + BITMAP "1111...." + BITMAP "11111..." + BITMAP "11111..." + BITMAP "11111..." + BITMAP "1..11111" + BITMAP "11111111" + BITMAP "33333333" + BITMAP ".333...." + + BITMAP ".333...." + BITMAP "3333...." + BITMAP ".333...." + BITMAP "3..3...." + BITMAP "333....." + BITMAP "333....." + BITMAP "111....." + BITMAP "22222..." + + BITMAP "11111111" + BITMAP "22222222" + BITMAP "11111111" + BITMAP "22222222" + BITMAP "11..1111" + BITMAP "22..2222" + BITMAP "11..1111" + BITMAP "22..2222" + + BITMAP "11..1111" + BITMAP "22..2222" + BITMAP "11..1111" + BITMAP "22..2222" + BITMAP "33..1111" + BITMAP "33......" + BITMAP "..3....." + BITMAP "........" + + BITMAP "11111111" + BITMAP "22222222" + BITMAP "11111111" + BITMAP "22222222" + BITMAP "1111..11" + BITMAP "2222..22" + BITMAP "1111..11" + BITMAP "2222..22" + + BITMAP "1111..11" + BITMAP "2222..22" + BITMAP "1111..11" + BITMAP "2222..22" + BITMAP "1111..33" + BITMAP "......33" + BITMAP ".....3.." + BITMAP "........" + + ' BUCKET (136/137) + ' Uses Palette 3 (2s=Blue $12, 3s=Brown $07) + ' 136 = Top Sprite (Water + rim/body) + BITMAP "22222222" ' Row 0 + BITMAP "22222222" ' Row 1 + BITMAP "33333333" ' Row 6 + BITMAP "33333333" ' Row 7 + BITMAP "33333333" ' Row 4 + BITMAP "33333333" ' Row 5 + BITMAP "33333333" ' Row 6 + BITMAP "33333333" ' Row 7 + + ' 137 = Bottom Sprite (Bucket continuation / can be empty if not needed) + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' BOMB (138) + BITMAP "...3...." + BITMAP "...2...." + BITMAP "..1111.." + BITMAP ".111111." + BITMAP "11111111" + BITMAP "11111111" + BITMAP ".111111." + BITMAP "..1111.." + + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' SPLASH FRAME A (140) + CHRROM PATTERN 140 + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "2......2" + BITMAP "..2..2.." + BITMAP ".222222." + BITMAP "..2222.." + BITMAP "...22..." + + ' SPLASH FRAME B (141) + CHRROM PATTERN 141 + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' PATTERN 146: CURSOR + ' Uses color 3 (Light Grey in Palette 3) + CHRROM PATTERN 146 + BITMAP "........" + BITMAP "..3333.." + BITMAP ".333333." + BITMAP ".333333." + BITMAP ".333333." + BITMAP ".333333." + BITMAP "..3333.." + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' PATTERN 40: BUILDING BLOCK (SOLID 3s = DARK GREY) + CHRROM PATTERN 40 + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + ' PATTERN 41: FLOOR MAIN AREA (SOLID 1s = GREEN) + CHRROM PATTERN 41 + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + ' PATTERN 42: DARK BLUE SKY (SOLID 2s = BLUE) + CHRROM PATTERN 42 + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + ' PATTERN 160: NUMBERS FOR SPRITES (Uses 3s = GREY) + ' ** FIXED **: Added blank spacers between digits to align with *2 logic + CHRROM PATTERN 160 + ' 0 + BITMAP ".33333.." + BITMAP "33...33." + BITMAP "33...33." + BITMAP "33...33." + BITMAP "33...33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "........" + + ' Spacer + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' 1 + BITMAP "...33..." + BITMAP "..333..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP ".333333." + BITMAP "........" + + ' Spacer + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' 2 + BITMAP ".33333.." + BITMAP "33...33." + BITMAP ".....33." + BITMAP "...33..." + BITMAP "..33...." + BITMAP "33......" + BITMAP "3333333." + BITMAP "........" + + ' Spacer + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' 3 + BITMAP "333333.." + BITMAP "....33.." + BITMAP "...33..." + BITMAP "....33.." + BITMAP ".....33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "........" + + ' Spacer + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' 4 + BITMAP "..333..." + BITMAP ".33.3..." + BITMAP "33..3..." + BITMAP "3333333." + BITMAP "....3..." + BITMAP "....3..." + BITMAP "....3..." + BITMAP "........" + + ' Spacer + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' 5 + BITMAP "3333333." + BITMAP "33......" + BITMAP "333333.." + BITMAP ".....33." + BITMAP ".....33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "........" + + ' Spacer + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' 6 + BITMAP ".33333.." + BITMAP "33......" + BITMAP "333333.." + BITMAP "33...33." + BITMAP "33...33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "........" + + ' Spacer + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' 7 + BITMAP "3333333." + BITMAP "33...33." + BITMAP "...33..." + BITMAP "..33...." + BITMAP "..33...." + BITMAP "..33...." + BITMAP "..33...." + BITMAP "........" + + ' Spacer + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' 8 + BITMAP ".33333.." + BITMAP "33...33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "33...33." + BITMAP "33...33." + BITMAP ".33333.." + BITMAP "........" + + ' Spacer + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' 9 + BITMAP ".33333.." + BITMAP "33...33." + BITMAP ".333333." + BITMAP ".....33." + BITMAP "....33.." + BITMAP "...33..." + BITMAP ".333...." + BITMAP "........" + + ' Spacer + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + + + diff --git a/contrib/atan2.bas b/contrib/atan2.bas new file mode 100644 index 0000000..74301d8 --- /dev/null +++ b/contrib/atan2.bas @@ -0,0 +1,143 @@ + ' + ' atan2 and getsincos functions for 256 degrees circles + ' + ' Contributed by Arturo Ragozini (ARTRAG) + ' + ' Added Jan/10/2026. + ' + +'''''''''''''''''''''''''''''''''''''''''''''''' +' ;;;;;;;; atan(2^(x/32))*128/pi ;;;;;;;; +atan_tab: + data $20,$20,$20,$21,$21,$22,$22,$23,$23,$23,$24,$24,$25,$25,$26,$26 + data $26,$27,$27,$28,$28,$28,$29,$29,$2A,$2A,$2A,$2B,$2B,$2C,$2C,$2C + data $2D,$2D,$2D,$2E,$2E,$2E,$2F,$2F,$2F,$30,$30,$30,$31,$31,$31,$31 + data $32,$32,$32,$32,$33,$33,$33,$33,$34,$34,$34,$34,$35,$35,$35,$35 + data $36,$36,$36,$36,$36,$37,$37,$37,$37,$37,$37,$38,$38,$38,$38,$38 + data $38,$39,$39,$39,$39,$39,$39,$39,$39,$3A,$3A,$3A,$3A,$3A,$3A,$3A + data $3A,$3B,$3B,$3B,$3B,$3B,$3B,$3B,$3B,$3B,$3B,$3B,$3C,$3C,$3C,$3C + data $3C,$3C,$3C,$3C,$3C,$3C,$3C,$3C,$3C,$3D,$3D,$3D,$3D,$3D,$3D,$3D + data $3D,$3D,$3D,$3D,$3D,$3D,$3D,$3D,$3D,$3D,$3D,$3D,$3E,$3E,$3E,$3E + data $3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E + data $3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3E,$3F,$3F,$3F,$3F + data $3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F + data $3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F + data $3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F + data $3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F + data $3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F,$3F + +' ;;;;;;;; log2(x)*32 ;;;;;;;; +log2_tab: + data $00,$00,$20,$32,$40,$4A,$52,$59,$60,$65,$6A,$6E,$72,$76,$79,$7D + data $80,$82,$85,$87,$8A,$8C,$8E,$90,$92,$94,$96,$98,$99,$9B,$9D,$9E + data $A0,$A1,$A2,$A4,$A5,$A6,$A7,$A9,$AA,$AB,$AC,$AD,$AE,$AF,$B0,$B1 + data $B2,$B3,$B4,$B5,$B6,$B7,$B8,$B9,$B9,$BA,$BB,$BC,$BD,$BD,$BE,$BF + data $C0,$C0,$C1,$C2,$C2,$C3,$C4,$C4,$C5,$C6,$C6,$C7,$C7,$C8,$C9,$C9 + data $CA,$CA,$CB,$CC,$CC,$CD,$CD,$CE,$CE,$CF,$CF,$D0,$D0,$D1,$D1,$D2 + data $D2,$D3,$D3,$D4,$D4,$D5,$D5,$D5,$D6,$D6,$D7,$D7,$D8,$D8,$D9,$D9 + data $D9,$DA,$DA,$DB,$DB,$DB,$DC,$DC,$DD,$DD,$DD,$DE,$DE,$DE,$DF,$DF + data $DF,$E0,$E0,$E1,$E1,$E1,$E2,$E2,$E2,$E3,$E3,$E3,$E4,$E4,$E4,$E5 + data $E5,$E5,$E6,$E6,$E6,$E7,$E7,$E7,$E7,$E8,$E8,$E8,$E9,$E9,$E9,$EA + data $EA,$EA,$EA,$EB,$EB,$EB,$EC,$EC,$EC,$EC,$ED,$ED,$ED,$ED,$EE,$EE + data $EE,$EE,$EF,$EF,$EF,$EF,$F0,$F0,$F0,$F1,$F1,$F1,$F1,$F1,$F2,$F2 + data $F2,$F2,$F3,$F3,$F3,$F3,$F4,$F4,$F4,$F4,$F5,$F5,$F5,$F5,$F5,$F6 + data $F6,$F6,$F6,$F7,$F7,$F7,$F7,$F7,$F8,$F8,$F8,$F8,$F9,$F9,$F9,$F9 + data $F9,$FA,$FA,$FA,$FA,$FA,$FB,$FB,$FB,$FB,$FB,$FC,$FC,$FC,$FC,$FC + data $FD,$FD,$FD,$FD,$FD,$FD,$FE,$FE,$FE,$FE,$FE,$FF,$FF,$FF,$FF,$FF + data $FF ' trick to cope with 256 + + +'----------------------------------------------- +' Source: https://www.msx.org/forum/msx-talk/development/8-bit-atan2?page=0 +' 8-bit atan2 +' Calculate the angle, in a 256-degree circle. +' The trick is to use logarithms to get the y/x ratio and +' integrate the power function into the atan table. +' input +' #dx_in, #dy_in in -256,255 +' +' output +' angle in 0-255 +' ^ +' q1 | q0 +'------+------> +' q3 | q2 +' | + + signed #dx_in + signed #dy_in + +atan2: procedure + if (#dy_in>0) then + if (#dx_in>0) then + gosub atan2_q0 ' q0 + else + #dx_in=-#dx_in ' q1 + gosub atan2_q0 + angle = (-angle) and $7F + return + end if + else + if (#dx_in>0) then + #dy_in=-#dy_in ' q2 + gosub atan2_q0 + angle = -angle + return + else + #dx_in=-#dx_in ' q3 + #dy_in=-#dy_in + gosub atan2_q0 + angle = angle + 128 + return + end if + end if + return + end + +atan2_q0: procedure + if (#dx_in>=#dy_in) then + angle = (-atan_tab(log2_tab(#dx_in)-log2_tab(#dy_in))) and $3F + else + angle = atan_tab(log2_tab(#dy_in)-log2_tab(#dx_in)) + end if + return + end + + + +';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +' input +' angle in 0-255 +' +' output +' #dy_out = sin(angle) in -256,256 +' #dx_out = cos(angle) in -256,256 + +getsincos: procedure + #dy_out = #sin_table(angle) + #dx_out = #cos_table(angle) + end + +#sin_table: + data 0, 6, 12, 18, 25, 31, 37, 43, 49, 56, 62, 68, 74, 80, 86, 92 + data 97, 103, 109, 115, 120, 126, 131, 136, 142, 147, 152, 157, 162, 167, 171, 176 + data 181, 185, 189, 193, 197, 201, 205, 209, 212, 216, 219, 222, 225, 228, 231, 234 + data 236, 238, 241, 243, 244, 246, 248, 249, 251, 252, 253, 254, 254, 255, 255, 255 +#cos_table: + data 256, 255, 255, 255, 254, 254, 253, 252, 251, 249, 248, 246, 244, 243, 241, 238 + data 236, 234, 231, 228, 225, 222, 219, 216, 212, 209, 205, 201, 197, 193, 189, 185 + data 181, 176, 171, 167, 162, 157, 152, 147, 142, 136, 131, 126, 120, 115, 109, 103 + data 97, 92, 86, 80, 74, 68, 62, 56, 49, 43, 37, 31, 25, 18, 12, 6 + data 0, -6, -12, -18, -25, -31, -37, -43, -49, -56, -62, -68, -74, -80, -86, -92 + data -97, -103, -109, -115, -120, -126, -131, -136, -142, -147, -152, -157, -162, -167, -171, -176 + data -181, -185, -189, -193, -197, -201, -205, -209, -212, -216, -219, -222, -225, -228, -231, -234 + data -236, -238, -241, -243, -244, -246, -248, -249, -251, -252, -253, -254, -254, -255, -255, -255 + data -256, -255, -255, -255, -254, -254, -253, -252, -251, -249, -248, -246, -244, -243, -241, -238 + data -236, -234, -231, -228, -225, -222, -219, -216, -212, -209, -205, -201, -197, -193, -189, -185 + data -181, -176, -171, -167, -162, -157, -152, -147, -142, -136, -131, -126, -120, -115, -109, -103 + data -97, -92, -86, -80, -74, -68, -62, -56, -49, -43, -37, -31, -25, -18, -12, -6 + data 0, 6, 12, 18, 25, 31, 37, 43, 49, 56, 62, 68, 74, 80, 86, 92 + data 97, 103, 109, 115, 120, 126, 131, 136, 142, 147, 152, 157, 162, 167, 171, 176 + data 181, 185, 189, 193, 197, 201, 205, 209, 212, 216, 219, 222, 225, 228, 231, 234 + data 236, 238, 241, 243, 244, 246, 248, 249, 251, 252, 253, 254, 254, 255, 255, 255 + diff --git a/contrib/cube.bas b/contrib/cube.bas new file mode 100644 index 0000000..8e4e6cc --- /dev/null +++ b/contrib/cube.bas @@ -0,0 +1,170 @@ +' -------------------------------------------------- +' Vector Cube Demo - XOR rotate Engine (CVBasic-clean) +' -------------------------------------------------- +' Matthew Eggleston. Feb/10/2026 + +MODE 1 + +' -------------------------------------------------- +' Animation variables +' -------------------------------------------------- +#ANGLE = 0 +#SIN_VAL = 0 + +CUBE_SIZE = 24 +DEPTH = 12 +CENTER_X = 80 +CENTER_Y = 88 ' <-- moved further UP + +' -------------------------------------------------- +' Main loop +' -------------------------------------------------- +WHILE 1 + GOSUB DrawCube + + WAIT + WAIT + + GOSUB DrawCube + + #ANGLE = #ANGLE + 6 + IF #ANGLE >= 360 THEN #ANGLE = 0 + + FOR I = 1 TO 16 + NEXT I +WEND + +' -------------------------------------------------- +' Draw cube routine +' -------------------------------------------------- +DrawCube: PROCEDURE + +GOSUB CalcSIN +#ROT = #SIN_VAL + +' Front square +FX1 = CENTER_X - CUBE_SIZE +FY1 = CENTER_Y - CUBE_SIZE +FX2 = CENTER_X + CUBE_SIZE +FY2 = CENTER_Y - CUBE_SIZE +FX3 = CENTER_X + CUBE_SIZE +FY3 = CENTER_Y + CUBE_SIZE +FX4 = CENTER_X - CUBE_SIZE +FY4 = CENTER_Y + CUBE_SIZE + +' Rotate front square +DX = #ROT / 3 +DY = #ROT / 4 +FX1 = FX1 + DX: FY1 = FY1 - DY +FX2 = FX2 + DY: FY2 = FY2 + DX +FX3 = FX3 - DX: FY3 = FY3 + DY +FX4 = FX4 - DY: FY4 = FY4 - DX + +' Back square +BX1 = FX1 + DEPTH +BY1 = FY1 - DEPTH +BX2 = FX2 + DEPTH +BY2 = FY2 - DEPTH +BX3 = FX3 + DEPTH +BY3 = FY3 - DEPTH +BX4 = FX4 + DEPTH +BY4 = FY4 - DEPTH + +' Draw edges +AX = FX1: AY = FY1: BX = FX2: BY = FY2: GOSUB Draw_Line_Sub +AX = FX2: AY = FY2: BX = FX3: BY = FY3: GOSUB Draw_Line_Sub +AX = FX3: AY = FY3: BX = FX4: BY = FY4: GOSUB Draw_Line_Sub +AX = FX4: AY = FY4: BX = FX1: BY = FY1: GOSUB Draw_Line_Sub + +AX = BX1: AY = BY1: BX = BX2: BY = BY2: GOSUB Draw_Line_Sub +AX = BX2: AY = BY2: BX = BX3: BY = BY3: GOSUB Draw_Line_Sub +AX = BX3: AY = BY3: BX = BX4: BY = BY4: GOSUB Draw_Line_Sub +AX = BX4: AY = BY4: BX = BX1: BY = BY1: GOSUB Draw_Line_Sub + +AX = FX1: AY = FY1: BX = BX1: BY = BY1: GOSUB Draw_Line_Sub +AX = FX2: AY = FY2: BX = BX2: BY = BY2: GOSUB Draw_Line_Sub +AX = FX3: AY = FY3: BX = BX3: BY = BY3: GOSUB Draw_Line_Sub +AX = FX4: AY = FY4: BX = BX4: BY = BY4: GOSUB Draw_Line_Sub + +RETURN +END + +' -------------------------------------------------- +' Integer SIN approximation +' -------------------------------------------------- +CalcSIN: PROCEDURE +#A = #ANGLE +IF #A < 90 THEN + #SIN_VAL = #A +ELSEIF #A < 180 THEN + #SIN_VAL = 180 - #A +ELSEIF #A < 270 THEN + #SIN_VAL = -(#A - 180) +ELSE + #SIN_VAL = -(360 - #A) +END IF +RETURN +END + +' -------------------------------------------------- +' Bresenham line (XOR) +' -------------------------------------------------- +SIGNED #ERR + +Draw_Line_Sub: PROCEDURE +IF BX < AX THEN + #DX = AX - BX : SX = -1 +ELSE + #DX = BX - AX : SX = 1 +END IF + +IF BY < AY THEN + #DY = AY - BY : SY = -1 +ELSE + #DY = BY - AY : SY = 1 +END IF + +IF #DX > #DY THEN + #ERR = 2 * #DY - #DX + WHILE 1 + GOSUB PSET_Sub + IF AX = BX THEN RETURN + IF #ERR < 0 THEN + #ERR = #ERR + 2 * #DY + ELSE + #ERR = #ERR + 2 * (#DY - #DX) + AY = AY + SY + END IF + AX = AX + SX + WEND +ELSE + #ERR = 2 * #DX - #DY + WHILE 1 + GOSUB PSET_Sub + IF AY = BY THEN RETURN + IF #ERR < 0 THEN + #ERR = #ERR + 2 * #DX + ELSE + #ERR = #ERR + 2 * (#DX - #DY) + AX = AX + SX + END IF + AY = AY + SY + WEND +END IF +RETURN +END + +' -------------------------------------------------- +' XOR pixel plot +' -------------------------------------------------- +PSET_Sub: PROCEDURE +#C = ((AX) AND $F8) + ((192-AY)/8*256) + ((192-AY) AND 7) +VPOKE #C, VPEEK(#C) XOR BIT_TABLE(AX AND 7) +RETURN +END + +' -------------------------------------------------- +' Bit table for XOR pixels +' -------------------------------------------------- +BIT_TABLE: +DATA BYTE $80,$40,$20,$10,$08,$04,$02,$01 diff --git a/cpu6502.c b/cpu6502.c index 1c44cbf..85062b9 100644 --- a/cpu6502.c +++ b/cpu6502.c @@ -12,18 +12,31 @@ #include #include "cvbasic.h" #include "node.h" +#include "cpu6502.h" + +#define REG_NONE 0 +#define REG_ALL (REG_ACC | REG_X | REG_Y | REG_TEMP) + +/* + ** Character used internally to mark precalculated array[index] addresses + */ +#define INDEX_ADD '[' +#define INDEX_ADD_STRING "[" static char temp2[MAX_LINE_SIZE]; static char cpu6502_line[MAX_LINE_SIZE]; -static char cpu6502_a[MAX_LINE_SIZE]; -static char cpu6502_x[MAX_LINE_SIZE]; -static char cpu6502_y[MAX_LINE_SIZE]; +static char cpu6502_a_value[MAX_LINE_SIZE]; +static char cpu6502_a_alias[MAX_LINE_SIZE]; +static char cpu6502_x_value[MAX_LINE_SIZE]; +static char cpu6502_x_alias[MAX_LINE_SIZE]; +static char cpu6502_y_value[MAX_LINE_SIZE]; +static char cpu6502_y_alias[MAX_LINE_SIZE]; +static char cpu6502_pointer_alias[MAX_LINE_SIZE]; static int cpu6502_flag_z_valid; static void cpu6502_emit_line(void); -static int cpu6502_8bit_simple(struct node *); void cpu6502_emit_line(void) { @@ -41,9 +54,13 @@ void cpu6502_label(char *label) { sprintf(cpu6502_line, "%s:\n", label); cpu6502_emit_line(); - cpu6502_a[0] = '\0'; - cpu6502_x[0] = '\0'; - cpu6502_y[0] = '\0'; + cpu6502_a_value[0] = '\0'; + cpu6502_a_alias[0] = '\0'; + cpu6502_x_value[0] = '\0'; + cpu6502_x_alias[0] = '\0'; + cpu6502_y_value[0] = '\0'; + cpu6502_y_alias[0] = '\0'; + cpu6502_pointer_alias[0] = '\0'; cpu6502_flag_z_valid = 0; } @@ -52,9 +69,13 @@ void cpu6502_label(char *label) */ void cpu6502_empty(void) { - cpu6502_a[0] = '\0'; - cpu6502_x[0] = '\0'; - cpu6502_y[0] = '\0'; + cpu6502_a_value[0] = '\0'; + cpu6502_a_alias[0] = '\0'; + cpu6502_x_value[0] = '\0'; + cpu6502_x_alias[0] = '\0'; + cpu6502_y_value[0] = '\0'; + cpu6502_y_alias[0] = '\0'; + cpu6502_pointer_alias[0] = '\0'; cpu6502_flag_z_valid = 0; } @@ -81,30 +102,36 @@ void cpu6502_noop(char *mnemonic) if (strcmp(mnemonic, "PHA") == 0 || strcmp(mnemonic, "SEI") == 0 || strcmp(mnemonic, "CLI") == 0 || strcmp(mnemonic, "SEC") == 0 || strcmp(mnemonic, "CLC") == 0) { /* Nothing to do */ } else if (strcmp(mnemonic, "PLA") == 0) { - cpu6502_a[0] = '\0'; + cpu6502_a_value[0] = '\0'; + cpu6502_a_alias[0] = '\0'; cpu6502_flag_z_valid = 1; } else if (strcmp(mnemonic, "TAX") == 0) { - strcpy(cpu6502_x, cpu6502_a); + strcpy(cpu6502_x_value, cpu6502_a_value); cpu6502_flag_z_valid = 1; } else if (strcmp(mnemonic, "TAY") == 0) { - strcpy(cpu6502_y, cpu6502_a); + strcpy(cpu6502_y_value, cpu6502_a_value); cpu6502_flag_z_valid = 1; } else if (strcmp(mnemonic, "TXA") == 0) { - strcpy(cpu6502_a, cpu6502_x); + strcpy(cpu6502_a_value, cpu6502_x_value); cpu6502_flag_z_valid = 1; } else if (strcmp(mnemonic, "TYA") == 0) { - strcpy(cpu6502_a, cpu6502_y); + strcpy(cpu6502_a_value, cpu6502_y_value); cpu6502_flag_z_valid = 1; } else if (strcmp(mnemonic, "INX") == 0 || strcmp(mnemonic, "DEX") == 0) { - cpu6502_x[0] = '\0'; + cpu6502_x_value[0] = '\0'; + cpu6502_x_alias[0] = '\0'; cpu6502_flag_z_valid = 0; } else if (strcmp(mnemonic, "INY") == 0 || strcmp(mnemonic, "DEY") == 0) { - cpu6502_y[0] = '\0'; + cpu6502_y_value[0] = '\0'; + cpu6502_y_alias[0] = '\0'; cpu6502_flag_z_valid = 0; } else if (strcmp(mnemonic, "RTS") == 0) { - cpu6502_a[0] = '\0'; - cpu6502_x[0] = '\0'; - cpu6502_y[0] = '\0'; + cpu6502_a_value[0] = '\0'; + cpu6502_a_alias[0] = '\0'; + cpu6502_x_value[0] = '\0'; + cpu6502_x_alias[0] = '\0'; + cpu6502_y_value[0] = '\0'; + cpu6502_y_alias[0] = '\0'; cpu6502_flag_z_valid = 0; } else { fprintf(stderr, "cpu6502_noop: not found mnemonic %s\n", mnemonic); @@ -128,94 +155,129 @@ void cpu6502_1op(char *mnemonic, char *operand) ** copy into registers using a single byte instruction. */ if (strcmp(mnemonic, "LDA") == 0) { - if (strcmp(operand, cpu6502_x) == 0) { + if (strcmp(operand, cpu6502_a_value) == 0 || strcmp(operand, cpu6502_a_alias) == 0) + return; + if (strcmp(operand, cpu6502_x_value) == 0 || strcmp(operand, cpu6502_x_alias) == 0) { cpu6502_noop("TXA"); + strcpy(cpu6502_a_value, cpu6502_x_value); + strcpy(cpu6502_a_alias, cpu6502_x_alias); return; } - if (strcmp(operand, cpu6502_y) == 0) { + if (strcmp(operand, cpu6502_y_value) == 0 || strcmp(operand, cpu6502_y_alias) == 0) { cpu6502_noop("TYA"); + strcpy(cpu6502_a_value, cpu6502_y_value); + strcpy(cpu6502_a_alias, cpu6502_y_alias); return; } } if (strcmp(mnemonic, "LDX") == 0) { - if (strcmp(operand, cpu6502_a) == 0) { + if (strcmp(operand, cpu6502_x_value) == 0 || strcmp(operand, cpu6502_x_alias) == 0) + return; + if (strcmp(operand, cpu6502_a_value) == 0 || strcmp(operand, cpu6502_a_alias) == 0) { cpu6502_noop("TAX"); + strcpy(cpu6502_x_value, cpu6502_a_value); + strcpy(cpu6502_x_alias, cpu6502_a_alias); return; } } if (strcmp(mnemonic, "LDY") == 0) { - if (strcmp(operand, cpu6502_a) == 0) { + if (strcmp(operand, cpu6502_y_value) == 0 || strcmp(operand, cpu6502_y_alias) == 0) + return; + if (strcmp(operand, cpu6502_a_value) == 0 || strcmp(operand, cpu6502_a_alias) == 0) { cpu6502_noop("TAY"); + strcpy(cpu6502_y_value, cpu6502_a_value); + strcpy(cpu6502_y_alias, cpu6502_a_alias); return; } } sprintf(cpu6502_line, "\t%s %s\n", mnemonic, operand); cpu6502_emit_line(); if (strcmp(mnemonic, "LDA") == 0) { - if (strchr(operand, ',') != NULL) - cpu6502_a[0] = '\0'; - else - strcpy(cpu6502_a, operand); + if (strchr(operand, ',') != NULL) { + cpu6502_a_value[0] = '\0'; + cpu6502_a_alias[0] = '\0'; + } else if (operand[0] == '#') { + strcpy(cpu6502_a_value, operand); + cpu6502_a_alias[0] = '\0'; + } else { + cpu6502_a_value[0] = '\0'; + strcpy(cpu6502_a_alias, operand); + } cpu6502_flag_z_valid = 1; } else if (strcmp(mnemonic, "LDX") == 0) { - if (strchr(operand, ',') != NULL) - cpu6502_x[0] = '\0'; - else - strcpy(cpu6502_x, operand); + if (strchr(operand, ',') != NULL) { + cpu6502_x_value[0] = '\0'; + cpu6502_x_alias[0] = '\0'; + } else if (operand[0] == '#') { + strcpy(cpu6502_x_value, operand); + cpu6502_x_alias[0] = '\0'; + } else { + cpu6502_x_value[0] = '\0'; + strcpy(cpu6502_x_alias, operand); + } cpu6502_flag_z_valid = 0; } else if (strcmp(mnemonic, "LDY") == 0) { - if (strchr(operand, ',') != NULL) - cpu6502_y[0] = '\0'; - else - strcpy(cpu6502_y, operand); + if (strchr(operand, ',') != NULL) { + cpu6502_y_value[0] = '\0'; + cpu6502_y_alias[0] = '\0'; + } else if (operand[0] == '#') { + strcpy(cpu6502_y_value, operand); + cpu6502_y_alias[0] = '\0'; + } else { + cpu6502_y_value[0] = '\0'; + strcpy(cpu6502_y_alias, operand); + } cpu6502_flag_z_valid = 0; } else if (strcmp(mnemonic, "CMP") == 0 || strcmp(mnemonic, "CPX") == 0 || strcmp(mnemonic, "CPY") == 0) { /* Only flags affected */ cpu6502_flag_z_valid = 0; } else if (strcmp(mnemonic, "ADC") == 0 || strcmp(mnemonic, "SBC") == 0 || strcmp(mnemonic, "ORA") == 0 || strcmp(mnemonic, "EOR") == 0 || strcmp(mnemonic, "AND") == 0) { - cpu6502_a[0] = '\0'; + cpu6502_a_value[0] = '\0'; + cpu6502_a_alias[0] = '\0'; cpu6502_flag_z_valid = 1; } else if (strcmp(mnemonic, "ROR") == 0 || strcmp(mnemonic, "ROL") == 0 || strcmp(mnemonic, "ASL") == 0 || strcmp(mnemonic, "LSR") == 0) { if (strcmp(operand, "A") == 0) { - cpu6502_a[0] = '\0'; + cpu6502_a_value[0] = '\0'; + cpu6502_a_alias[0] = '\0'; cpu6502_flag_z_valid = 1; } else { - if (cpu6502_a[0] != '#') - cpu6502_a[0] = '\0'; - if (cpu6502_x[0] != '#') - cpu6502_x[0] = '\0'; - if (cpu6502_y[0] != '#') - cpu6502_y[0] = '\0'; + cpu6502_a_alias[0] = '\0'; + cpu6502_x_alias[0] = '\0'; + cpu6502_y_alias[0] = '\0'; cpu6502_flag_z_valid = 0; } } else if (strcmp(mnemonic, "INC") == 0 || strcmp(mnemonic, "DEC") == 0) { - if (cpu6502_a[0] != '#') - cpu6502_a[0] = '\0'; - if (cpu6502_x[0] != '#') - cpu6502_x[0] = '\0'; - if (cpu6502_y[0] != '#') - cpu6502_y[0] = '\0'; + cpu6502_a_alias[0] = '\0'; + cpu6502_x_alias[0] = '\0'; + cpu6502_y_alias[0] = '\0'; cpu6502_flag_z_valid = 0; - } else if (strcmp(mnemonic, "JSR") == 0 || strcmp(mnemonic, "JMP") == 0) { - cpu6502_a[0] = '\0'; - cpu6502_x[0] = '\0'; - cpu6502_y[0] = '\0'; + } else if (strcmp(mnemonic, "JSR") == 0 || strcmp(mnemonic, "JMP") == 0 || strcmp(mnemonic, "ORG") == 0 || strcmp(mnemonic, "FORG") == 0) { + cpu6502_a_value[0] = '\0'; + cpu6502_a_alias[0] = '\0'; + cpu6502_x_value[0] = '\0'; + cpu6502_x_alias[0] = '\0'; + cpu6502_y_value[0] = '\0'; + cpu6502_y_alias[0] = '\0'; + cpu6502_pointer_alias[0] = '\0'; cpu6502_flag_z_valid = 0; } else if (strcmp(mnemonic, "STA") == 0) { - if (strchr(operand, ',') != NULL) - cpu6502_a[0] = '\0'; - else - strcpy(cpu6502_a, operand); + if (strchr(operand, ',') != NULL) { + cpu6502_a_alias[0] = '\0'; + } else { + strcpy(cpu6502_a_alias, operand); + } } else if (strcmp(mnemonic, "STX") == 0) { - if (strchr(operand, ',') != NULL) - cpu6502_x[0] = '\0'; - else - strcpy(cpu6502_x, operand); + if (strchr(operand, ',') != NULL) { + cpu6502_x_alias[0] = '\0'; + } else { + strcpy(cpu6502_x_alias, operand); + } } else if (strcmp(mnemonic, "STY") == 0) { - if (strchr(operand, ',') != NULL) - cpu6502_y[0] = '\0'; - else - strcpy(cpu6502_y, operand); + if (strchr(operand, ',') != NULL) { + cpu6502_y_alias[0] = '\0'; + } else { + strcpy(cpu6502_y_alias, operand); + } } else if (strcmp(mnemonic, "BEQ") == 0 || strcmp(mnemonic, "BEQ.L") == 0 || strcmp(mnemonic, "BNE") == 0 || strcmp(mnemonic, "BNE.L") == 0 || strcmp(mnemonic, "BCC") == 0 || strcmp(mnemonic, "BCC.L") == 0 @@ -235,50 +297,402 @@ void cpu6502_1op(char *mnemonic, char *operand) */ void cpu6502_node_label(struct node *node) { - /* Nothing to do, yet */ -} - -/* - ** Detect operations that only use A. - */ -static int cpu6502_8bit_simple(struct node *node) -{ + struct node *explore; + int stack; + int c; + char *p; + switch (node->type) { - case N_LOAD8: - case N_NUM8: - case N_INP: - case N_JOY1: - case N_JOY2: - case N_KEY1: - case N_KEY2: - case N_MUSIC: - case N_NTSC: - case N_VDPSTATUS: - return 1; - case N_NEG8: - case N_NOT8: - return cpu6502_8bit_simple(node->left); - case N_LESSEQUAL8: /* 8-bit <= */ - case N_GREATER8: /* 8-bit > */ - if (node->left->type != N_NUM8 && node->left->type != N_LOAD8) - return 0; - return cpu6502_8bit_simple(node->right); + case N_USR: /* Assembly language function with result */ + if (node->left != NULL) + cpu6502_node_label(node->left->left); + node->regs = REG_ALL; + break; + case N_LOAD8: /* Load 8-bit value from address */ + case N_NUM8: /* Load 8-bit constant */ + case N_INP: /* Read port */ + case N_JOY1: /* Read joystick 1 */ + case N_JOY2: /* Read joystick 2 */ + case N_KEY1: /* Read keypad 1 */ + case N_KEY2: /* Read keypad 2 */ + case N_MUSIC: /* Read music playing status */ + case N_NTSC: /* Read NTSC flag */ + case N_VDPSTATUS: /* Read VDP status */ + node->regs = REG_ACC; + break; + case N_ADDR: /* Get address of variable */ + case N_POS: /* Get screen cursor position */ + case N_READ8: /* Read 8-bit value */ + case N_READ16: /* Read 16-bit value */ + case N_LOAD16: /* Load 16-bit value from address */ + case N_NUM16: /* Load 16-bit constant */ + case N_FRAME: /* Read current frame number */ + node->regs = REG_ACC | REG_Y; + break; + case N_RANDOM: /* Read pseudorandom generator */ + node->regs = REG_ACC | REG_Y | REG_TEMP; + break; + case N_NEG8: /* Negate 8-bit value */ + case N_NOT8: /* Complement 8-bit value */ + case N_REDUCE16: /* Reduce 16-bit value to 8-bit */ + case N_VPEEK: /* Read VRAM */ + cpu6502_node_label(node->left); + node->regs = node->left->regs; + break; + case N_NEG16: /* Negate 16-bit value */ + case N_NOT16: /* Complement 16-bit value */ + case N_ABS16: /* Get absolute 16-bit value */ + cpu6502_node_label(node->left); + node->regs = node->left->regs | REG_X; + break; + case N_SGN16: /* Get sign of 16-bit value */ + cpu6502_node_label(node->left); + node->regs = node->left->regs | REG_X | REG_TEMP; + break; + case N_EXTEND8S: /* Extend 8-bit signed value to 16-bit */ + cpu6502_node_label(node->left); + node->regs = node->left->regs | REG_X | REG_Y; + break; + case N_EXTEND8: /* Extend 8-bit value to 16-bit */ + cpu6502_node_label(node->left); + node->regs = node->left->regs | REG_Y; + break; + case N_PEEK8: /* Load 8-bit content */ + if (node->left->type == N_ADDR) { /* Optimize address */ + node->regs = REG_ACC; + break; + } + if ((node->left->type == N_PLUS16 || node->left->type == N_MINUS16) + && node->left->left->type == N_ADDR + && node->left->right->type == N_NUM16) { /* Optimize address plus constant */ + node->regs = REG_ACC; + break; + } + cpu6502_node_label(node->left); + node->regs = node->left->regs; + break; + case N_PEEK16: /* Load 16-bit content */ + if (node->left->type == N_ADDR) { /* Optimize address */ + node->regs = REG_ACC | REG_Y; + break; + } + if ((node->left->type == N_PLUS16 || node->left->type == N_MINUS16) + && node->left->left->type == N_ADDR + && node->left->right->type == N_NUM16) { /* Optimize address plus constant */ + node->regs = REG_ACC | REG_Y; + break; + } + cpu6502_node_label(node->left); + node->regs = node->left->regs; + break; case N_OR8: /* 8-bit OR */ case N_XOR8: /* 8-bit XOR */ case N_AND8: /* 8-bit AND */ case N_EQUAL8: /* 8-bit = */ case N_NOTEQUAL8: /* 8-bit <> */ case N_LESS8: /* 8-bit < */ + case N_LESSEQUAL8: /* 8-bit <= */ + case N_GREATER8: /* 8-bit > */ case N_GREATEREQUAL8: /* 8-bit >= */ case N_PLUS8: /* 8-bit + */ case N_MINUS8: /* 8-bit - */ case N_MUL8: /* 8-bit * */ case N_DIV8: /* 8-bit / */ - if (node->right->type != N_NUM8 && node->right->type != N_LOAD8) - return 0; - return cpu6502_8bit_simple(node->left); - default: - return 0; + if (node->type == N_MUL8 && node->right->type == N_NUM8 && is_power_of_two(node->right->value)) { + cpu6502_node_label(node->left); + node->regs = node->left->regs; + break; + } + if (node->type == N_DIV8 && node->right->type == N_NUM8 && is_power_of_two(node->right->value)) { + cpu6502_node_label(node->left); + node->regs = node->left->regs; + break; + } + if (node->type == N_LESSEQUAL8 || node->type == N_GREATER8) { + if (node->left->type == N_NUM8) { + cpu6502_node_label(node->right); + node->regs = node->right->regs; + } else if (node->left->type == N_LOAD8) { + cpu6502_node_label(node->right); + node->regs = node->right->regs; + } else { + cpu6502_node_label(node->right); + cpu6502_node_label(node->left); + node->regs = node->left->regs | node->right->regs | REG_TEMP; + } + } else if (node->right->type == N_LOAD8) { + cpu6502_node_label(node->left); + node->regs = node->left->regs; + } else if (node->right->type == N_NUM8) { + cpu6502_node_label(node->left); + node->regs = node->left->regs; + } else { + cpu6502_node_label(node->left); + cpu6502_node_label(node->right); + node->regs = node->left->regs | node->right->regs | REG_TEMP; + } + break; + case N_ASSIGN8: /* 8-bit assignment */ + if ((node->left->type == N_PLUS8 || node->left->type == N_MINUS8 || node->left->type == N_OR8 || node->left->type == N_AND8 || node->left->type == N_XOR8) + && (node->left->right->type == N_NUM8 || node->left->right->type == N_LOAD8) + && node_same_address(node->left->left, node->right)) { + if (node->right->type == N_ADDR) { + node->regs = REG_NONE; + c = 0; + } else if (((node->right->type == N_PLUS16 || node->right->type == N_MINUS16) && node->right->left->type == N_ADDR && node->right->right->type == N_NUM16)) { + node->regs = REG_NONE; + c = 0; + } else { + if (node->right->type == N_PLUS16 && node->right->left->type == N_ADDR && node->right->right->type == N_EXTEND8 && node->right->right->left->type == N_LOAD8) { + node->regs = REG_ACC | REG_TEMP | REG_Y; + } else { + cpu6502_node_label(node->right); + node->regs = node->right->regs; + } + c = 1; + node->regs |= REG_Y; + } + if (c == 0 && (node->left->type == N_PLUS8 || node->left->type == N_MINUS8) && node->left->right->type == N_NUM8 && node->left->right->value < 4) { + } else { + node->regs |= REG_ACC; + } + break; + } + + if (node->right->type == N_ADDR + || ((node->right->type == N_PLUS16 || node->right->type == N_MINUS16) && node->right->left->type == N_ADDR && node->right->right->type == N_NUM16)) { + cpu6502_node_label(node->left); + node->regs = node->left->regs; + break; + } + cpu6502_node_label(node->right); + cpu6502_node_label(node->left); + node->regs = node->right->regs | node->left->regs | REG_TEMP; + break; + case N_ASSIGN16: /* 16-bit assignment */ + if (node->right->type == N_ADDR) { + c = 1; + } else if (((node->right->type == N_PLUS16 || node->right->type == N_MINUS16) && node->right->left->type == N_ADDR && node->right->right->type == N_NUM16)) { + c = 1; + } else { + c = 0; + } + if (node->left->type == N_PLUS16 + && node->left->right->type == N_NUM16 + && ((c && node->left->right->value == 1) || !c) + && node_same_address(node->left->left, node->right)) { + if (node->right->type == N_ADDR) { + node->regs = REG_NONE; + c = 0; + } else if (((node->right->type == N_PLUS16 || node->right->type == N_MINUS16) && node->right->left->type == N_ADDR && node->right->right->type == N_NUM16)) { + node->regs = REG_NONE; + c = 0; + } else { + cpu6502_node_label(node->right); + node->regs = node->right->regs; + c = 1; + } + break; + } + + if (node->right->type == N_ADDR + || ((node->right->type == N_PLUS16 || node->right->type == N_MINUS16) && node->right->left->type == N_ADDR && node->right->right->type == N_NUM16)) { + cpu6502_node_label(node->left); + node->regs = node->left->regs; + break; + } + if (node->left->type == N_NUM16 || node->left->type == N_LOAD16) { + cpu6502_node_label(node->right); + cpu6502_node_label(node->left); + node->regs = node->right->regs | node->left->regs | REG_X | REG_TEMP; + } else { + cpu6502_node_label(node->left); + cpu6502_node_label(node->right); + node->regs = node->right->regs | node->left->regs | REG_TEMP; + } + break; + default: /* Every other node, all remaining are 16-bit operations */ + /* Optimization of address plus/minus constant */ + if (node->type == N_PLUS16 || node->type == N_MINUS16) { + if (node->left->type == N_ADDR) { + if (node->right->type == N_NUM16) { + node->regs = REG_ACC | REG_Y; + break; + } else if (node->right->type == N_EXTEND8 && node->right->left->type == N_LOAD8) { + node->regs = REG_ACC | REG_X | REG_Y; + break; + } + } + } + if (node->type == N_PLUS16) { + if (node->left->type == N_ADDR) { + cpu6502_node_label(node->right); + node->regs = node->right->regs | REG_ACC | REG_X | REG_Y; + break; + } + if (node->left->type == N_NUM16 || node->right->type == N_NUM16) { + if (node->left->type == N_NUM16) + explore = node->left; + else + explore = node->right; + if (node->left != explore) { + cpu6502_node_label(node->left); + node->regs = node->left->regs; + } else { + cpu6502_node_label(node->right); + node->regs = node->right->regs; + } + c = explore->value; + if ((c & 0xff) == 0 && (c & 0xff00) < 0x0800) { + break; + } + node->regs |= REG_X; + break; + } + } + if (node->type == N_MINUS16) { + if (node->right->type == N_NUM16) + explore = node->right; + else + explore = NULL; + if (explore != NULL) { + int c = explore->value; + + cpu6502_node_label(node->left); + node->regs = node->left->regs; + if ((c & 0xff) == 0 && (c & 0xff00) < 0x0800) { + break; + } + node->regs |= REG_X; + break; + } + } + if (node->type == N_OR16 || node->type == N_AND16 || node->type == N_XOR16) { + if (node->right->type == N_NUM16) + explore = node->right; + else + explore = NULL; + if (explore != NULL) { + int value = explore->value; + int byte; + char *mnemonic; + + if (node->left != explore) { + cpu6502_node_label(node->left); + node->regs = node->left->regs; + } else { + cpu6502_node_label(node->right); + node->regs = node->right->regs; + } + byte = (value >> 8) & 0xff; + if ((node->type == N_OR16 || node->type == N_XOR16) && byte == 0x00) { + /* Nothing to do :) */ + } else if (node->type == N_AND16 && byte == 0xff) { + /* Nothing to do :) */ + } else if (node->type == N_AND16 && byte == 0x00) { + } else if (node->type == N_OR16 && byte == 0xff) { + } else { + node->regs |= REG_X; + } + break; + } + } + if (node->type == N_MUL16) { + if (node->left->type == N_NUM16) + explore = node->left; + else if (node->right->type == N_NUM16) + explore = node->right; + else + explore = NULL; + if (explore != NULL && (explore->value == 0 || explore->value == 1 || is_power_of_two(explore->value))) { + c = explore->value; + if (c == 0) { + node->regs = REG_ACC | REG_Y; + } else { + if (node->left != explore) + explore = node->left; + else + explore = node->right; + if (c == 2 && explore->type == N_EXTEND8) { + cpu6502_node_label(explore->left); + explore->regs = explore->left->regs; + node->regs = explore->regs; + break; + } + if (c >= 256) { + if (explore->type == N_EXTEND8 || explore->type == N_EXTEND8S) { + cpu6502_node_label(explore->left); + explore->regs = explore->left->regs | REG_Y; + } else { + cpu6502_node_label(explore); + explore->regs |= REG_Y; + } + c /= 256; + } else { + cpu6502_node_label(explore); + } + node->regs = explore->regs; + } + break; + } + } + if (node->type == N_DIV16) { + if (node->right->type == N_NUM16 && (node->right->value == 2 || node->right->value == 4 || node->right->value == 8)) { + cpu6502_node_label(node->left); + node->regs = node->left->regs | REG_TEMP; + break; + } + } + if (node->type == N_LESSEQUAL16 || node->type == N_GREATER16) { + if (node->left->type == N_NUM16) { + cpu6502_node_label(node->right); + node->regs = node->right->regs; + stack = 0; + } else if (node->left->type == N_LOAD16) { + cpu6502_node_label(node->right); + node->regs = node->right->regs; + stack = 0; + } else if (node->left->type == N_EXTEND8 && node->left->left->type == N_LOAD8) { + cpu6502_node_label(node->right); + node->regs = node->right->regs; + stack = 0; + } else { + cpu6502_node_label(node->right); + cpu6502_node_label(node->left); + node->regs = node->right->regs | node->left->regs | REG_TEMP; + stack = 1; + } + } else { + if (node->right->type == N_NUM16) { + cpu6502_node_label(node->left); + node->regs = node->left->regs; + stack = 0; + } else if (node->right->type == N_LOAD16) { + cpu6502_node_label(node->left); + node->regs = node->left->regs; + stack = 0; + } else if (node->right->type == N_EXTEND8 && node->right->left->type == N_LOAD8) { + cpu6502_node_label(node->left); + node->regs = node->left->regs; + stack = 0; + } else { + cpu6502_node_label(node->left); + cpu6502_node_label(node->right); + node->regs = node->left->regs | node->right->regs | REG_TEMP; + stack = 1; + } + } + if (node->type == N_OR16 || node->type == N_XOR16 || node->type == N_AND16) { + if (!stack) { + node->regs |= REG_X; + } + } else if (node->type == N_EQUAL16 || node->type == N_NOTEQUAL16) { + if (!stack) + node->regs |= REG_TEMP; + } else { + node->regs |= REG_X; + } + break; } } @@ -416,6 +830,14 @@ void cpu6502_node_generate(struct node *node, int decision) } cpu6502_node_generate(node->left, 0); cpu6502_1op("JSR", "_peek8"); + node = node->left; + if (node->type == N_PLUS16 && node->left->type == N_ADDR && node->right->type == N_EXTEND8 && node->right->left->type == N_LOAD8) { /* addr + var : addr - var */ + node_get_label(node->left, 0); + strcpy(cpu6502_pointer_alias, temp); + strcat(cpu6502_pointer_alias, INDEX_ADD_STRING); + node_get_label(node->right->left, 0); + strcat(cpu6502_pointer_alias, temp); + } break; case N_PEEK16: /* Load 16-bit content */ if (node->left->type == N_ADDR) { /* Optimize address */ @@ -521,11 +943,17 @@ void cpu6502_node_generate(struct node *node, int decision) cpu6502_node_generate(node->right, 0); node_get_label(node->left, 0); } else { - cpu6502_node_generate(node->right, 0); - cpu6502_noop("PHA"); - cpu6502_node_generate(node->left, 0); - cpu6502_1op("STA", "temp"); - cpu6502_noop("PLA"); + if ((node->right->regs & REG_TEMP) == 0) { + cpu6502_node_generate(node->left, 0); + cpu6502_1op("STA", "temp"); + cpu6502_node_generate(node->right, 0); + } else { + cpu6502_node_generate(node->right, 0); + cpu6502_noop("PHA"); + cpu6502_node_generate(node->left, 0); + cpu6502_1op("STA", "temp"); + cpu6502_noop("PLA"); + } strcpy(temp, "temp"); } } else if (node->right->type == N_LOAD8) { @@ -536,11 +964,17 @@ void cpu6502_node_generate(struct node *node, int decision) cpu6502_node_generate(node->left, 0); sprintf(temp, "#%d", c); } else { - cpu6502_node_generate(node->left, 0); - cpu6502_noop("PHA"); - cpu6502_node_generate(node->right, 0); - cpu6502_1op("STA", "temp"); - cpu6502_noop("PLA"); + if ((node->left->regs & REG_TEMP) == 0) { + cpu6502_node_generate(node->right, 0); + cpu6502_1op("STA", "temp"); + cpu6502_node_generate(node->left, 0); + } else { + cpu6502_node_generate(node->left, 0); + cpu6502_noop("PHA"); + cpu6502_node_generate(node->right, 0); + cpu6502_1op("STA", "temp"); + cpu6502_noop("PLA"); + } strcpy(temp, "temp"); } if (node->type == N_OR8) { @@ -719,7 +1153,7 @@ void cpu6502_node_generate(struct node *node, int decision) break; } - if (cpu6502_8bit_simple(node->left)) { + if ((node->left->regs & REG_TEMP) == 0) { cpu6502_node_generate(node->right, 0); cpu6502_1op("STA", "temp"); cpu6502_1op("STY", "temp+1"); @@ -816,7 +1250,7 @@ void cpu6502_node_generate(struct node *node, int decision) cpu6502_1op("STY", temp); break; } - if (node->left->type == N_NUM16 || node->left->type == N_LOAD16) { + if ((node->left->regs & REG_TEMP) == 0) { cpu6502_node_generate(node->right, 0); cpu6502_1op("STA", "temp"); cpu6502_1op("STY", "temp+1"); @@ -870,7 +1304,26 @@ void cpu6502_node_generate(struct node *node, int decision) sprintf(p, "%d)>>8", node->right->value); cpu6502_1op("LDY", temp); break; - } else if (node->right->type == N_EXTEND8 && node->right->left->type == N_LOAD8) { + } else if (node->right->type == N_EXTEND8 && node->right->left->type == N_LOAD8) { /* addr + var : addr - var */ + char *p; + + if ((p = strchr(cpu6502_pointer_alias, INDEX_ADD)) != NULL) { /* If there is an index saved... */ + node_get_label(node->right->left, 0); + if (strcmp(temp, p + 1) == 0) { /* ...and it is the same index */ + *p = '\0'; + node_get_label(node->left, 0); + if (strcmp(cpu6502_pointer_alias, temp) == 0) { + cpu6502_1op("LDA", "pointer"); + cpu6502_1op("LDY", "pointer+1"); + node_get_label(node->left, 0); + strcpy(cpu6502_pointer_alias, temp); + strcat(cpu6502_pointer_alias, INDEX_ADD_STRING); + node_get_label(node->right->left, 0); + strcat(cpu6502_pointer_alias, temp); + break; + } + } + } node_get_label(node->left, 2); cpu6502_1op("LDA", temp); cpu6502_noop("CLC"); @@ -1099,8 +1552,6 @@ void cpu6502_node_generate(struct node *node, int decision) cpu6502_noop("TYA"); cpu6502_noop("PHA"); cpu6502_node_generate(node->left, 0); - cpu6502_1op("STA", "temp"); - cpu6502_1op("STY", "temp+1"); stack = 1; } } else { @@ -1126,13 +1577,13 @@ void cpu6502_node_generate(struct node *node, int decision) cpu6502_noop("TYA"); cpu6502_noop("PHA"); cpu6502_node_generate(node->right, 0); - cpu6502_1op("STA", "temp"); - cpu6502_1op("STY", "temp+1"); stack = 1; } } if (node->type == N_OR16) { if (stack) { + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); cpu6502_noop("PLA"); cpu6502_1op("ORA", "temp+1"); cpu6502_noop("TAY"); @@ -1155,6 +1606,8 @@ void cpu6502_node_generate(struct node *node, int decision) } } else if (node->type == N_XOR16) { if (stack) { + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); cpu6502_noop("PLA"); cpu6502_1op("EOR", "temp+1"); cpu6502_noop("TAY"); @@ -1177,6 +1630,8 @@ void cpu6502_node_generate(struct node *node, int decision) } } else if (node->type == N_AND16) { if (stack) { + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); cpu6502_noop("PLA"); cpu6502_1op("AND", "temp+1"); cpu6502_noop("TAY"); @@ -1199,6 +1654,8 @@ void cpu6502_node_generate(struct node *node, int decision) } } else if (node->type == N_EQUAL16) { if (stack) { + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); cpu6502_noop("PLA"); cpu6502_noop("TAY"); cpu6502_noop("PLA"); @@ -1235,6 +1692,8 @@ void cpu6502_node_generate(struct node *node, int decision) } } else if (node->type == N_NOTEQUAL16) { if (stack) { + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); cpu6502_noop("PLA"); cpu6502_noop("TAY"); cpu6502_noop("PLA"); @@ -1271,6 +1730,8 @@ void cpu6502_node_generate(struct node *node, int decision) } } else if (node->type == N_LESS16 || node->type == N_GREATER16) { if (stack) { + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); cpu6502_noop("PLA"); cpu6502_noop("TAY"); cpu6502_noop("PLA"); @@ -1294,6 +1755,8 @@ void cpu6502_node_generate(struct node *node, int decision) } } else if (node->type == N_LESSEQUAL16 || node->type == N_GREATEREQUAL16) { if (stack) { + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); cpu6502_noop("PLA"); cpu6502_noop("TAY"); cpu6502_noop("PLA"); @@ -1318,6 +1781,8 @@ void cpu6502_node_generate(struct node *node, int decision) } } else if (node->type == N_PLUS16) { if (stack) { + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); cpu6502_noop("PLA"); cpu6502_noop("TAY"); cpu6502_noop("PLA"); @@ -1339,6 +1804,8 @@ void cpu6502_node_generate(struct node *node, int decision) } } else if (node->type == N_MINUS16) { if (stack) { + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); cpu6502_noop("PLA"); cpu6502_noop("TAY"); cpu6502_noop("PLA"); @@ -1359,14 +1826,17 @@ void cpu6502_node_generate(struct node *node, int decision) cpu6502_noop("TXA"); } } else { - if (!stack) { - cpu6502_noop("PHA"); - cpu6502_noop("TYA"); - cpu6502_noop("PHA"); - cpu6502_1op("LDA", temp); - cpu6502_1op("LDY", temp2); + if (stack) { cpu6502_1op("STA", "temp"); cpu6502_1op("STY", "temp+1"); + cpu6502_noop("PLA"); + cpu6502_noop("TAY"); + cpu6502_noop("PLA"); + } else { + cpu6502_1op("LDX", temp); + cpu6502_1op("STX", "temp"); + cpu6502_1op("LDX", temp2); + cpu6502_1op("STX", "temp+1"); } if (node->type == N_MUL16) { cpu6502_1op("JSR", "_mul16"); diff --git a/cpu6502.h b/cpu6502.h index cfca3d3..1124b2f 100644 --- a/cpu6502.h +++ b/cpu6502.h @@ -6,6 +6,11 @@ ** Creation date: Aug/04/2024. Separated from CVBasic.c */ +#define REG_ACC 1 +#define REG_X 2 +#define REG_Y 4 +#define REG_TEMP 8 + extern void cpu6502_dump(void); extern void cpu6502_label(char *); extern void cpu6502_empty(void); diff --git a/cpu9900.c b/cpu9900.c index 22515f8..f23272e 100644 --- a/cpu9900.c +++ b/cpu9900.c @@ -12,6 +12,9 @@ #include #include "cvbasic.h" #include "node.h" +#include "cpu9900.h" + +#define REG_ALL (REG_0 | REG_1 | REG_2 | REG_3 | REG_4 | REG_5 | REG_6 | REG_7) /* ** Conventions of register usage: @@ -46,7 +49,11 @@ static char op1[128], op2[128], op3[128], op4[128]; static char s1[128], s2[128], s3[128], s4[128], s5[128], s6[128], s7[128], s8[128]; static void cpu9900_emit_line(void); +static void nomatch(char *); static int getargument(char *src, char *dest, int start); +static int skipcomma(char *src, int start); +static void parseline(char *buf, char *op, char *s1, char *s2); +static int loadsr0(char *op, char *s1, char *s2); // set to a unique string that will never match void nomatch(char *src) @@ -76,7 +83,7 @@ int getargument(char *src, char *dest, int start) *dest = '\0'; - return src - p; + return (int) (src - p); } /* @@ -91,7 +98,7 @@ int skipcomma(char *src, int start) ++src; if (*src == ',') ++src; /* skip past */ - return src - p; + return (int) (src - p); } /* @@ -293,7 +300,7 @@ void cpu9900_emit_line(void) // first check - labels cancels all bets. We can try to get smarter with the registers like // the other ports later... if (buf[0] > ' ') { - strcpy(last_r0_load,""); + strcpy(last_r0_load, ""); } else { // is r0 the target? if (strcmp(op1, "mov") == 0 && strcmp(s2, "r0") == 0) { @@ -571,7 +578,295 @@ void cpu9900_2op(char *mnemonic, char *operand1, char *operand2) */ void cpu9900_node_label(struct node *node) { - /* Nothing to do, yet */ + struct node *explore; + + switch (node->type) { + case N_USR: /* Assembly language function with result */ + explore = node->left; + while (explore != NULL) { + cpu9900_node_label(explore->left); + explore = explore->right; + } + node->regs = REG_ALL; + break; + case N_ADDR: /* Get address of variable into r0 */ + case N_POS: /* Get screen cursor position in r0 (AAYY) */ + case N_LOAD8: /* Load 8-bit value from address */ + case N_LOAD16: /* Load 16-bit value from address */ + case N_NUM8: /* Load 8-bit constant */ + case N_NUM16: /* Load 16-bit constant */ + case N_INP: /* Read port */ + case N_JOY1: /* Read joystick 1 */ + case N_JOY2: /* Read joystick 2 */ + case N_KEY1: /* Read keypad 1 */ + case N_KEY2: /* Read keypad 2 */ + case N_MUSIC: /* Read music playing status */ + case N_NTSC: /* Read NTSC flag */ + case N_VDPSTATUS: /* Read VDP status */ + case N_FRAME: /* Read current frame number */ + node->regs = REG_0; + break; + case N_NEG8: /* Negate 8-bit value in r0 */ + case N_NOT8: /* Complement 8-bit value */ + case N_NEG16: /* Negate 16-bit value */ + case N_NOT16: /* Complement 16-bit value */ + case N_ABS16: /* Get absolute 16-bit value */ + case N_SGN16: /* Get sign of 16-bit value */ + case N_EXTEND8S: /* Extend 8-bit signed value to 16-bit */ + case N_EXTEND8: /* Extend 8-bit value to 16-bit */ + case N_REDUCE16: /* Reduce 16-bit value to 8-bit */ + case N_PEEK8: /* Load 8-bit content */ + case N_PEEK16: /* Load 16-bit content - must be aligned! */ + case N_VPEEK: /* Read VRAM */ + cpu9900_node_label(node->left); + node->regs = node->left->regs; + break; + case N_READ8: /* Read 8-bit value from read_pointer */ + case N_READ16: /* Read 16-bit value - warning, will not work as expected unaligned! */ + node->regs = REG_0 | REG_1; + break; + case N_RANDOM: /* Read pseudorandom generator */ + node->regs = REG_0 | REG_3 | REG_4; + break; + case N_OR8: /* 8-bit OR */ + case N_XOR8: /* 8-bit XOR */ + case N_AND8: /* 8-bit AND */ + case N_EQUAL8: /* 8-bit = */ + case N_NOTEQUAL8: /* 8-bit <> */ + case N_LESS8: /* 8-bit < */ + case N_LESSEQUAL8: /* 8-bit <= */ + case N_GREATER8: /* 8-bit > */ + case N_GREATEREQUAL8: /* 8-bit >= */ + case N_PLUS8: /* 8-bit + */ + case N_MINUS8: /* 8-bit - */ + case N_MUL8: /* 8-bit * */ + case N_DIV8: /* 8-bit / */ + if (node->type == N_PLUS8 && node->right->type == N_NUM8) { + cpu9900_node_label(node->left); + break; + } + if (node->type == N_MINUS8 && node->right->type == N_NUM8) { + cpu9900_node_label(node->left); + break; + } + if (node->type == N_MUL8 && node->right->type == N_NUM8 && is_power_of_two(node->right->value)) { + cpu9900_node_label(node->left); + break; + } + if (node->type == N_DIV8 && node->right->type == N_NUM8 && is_power_of_two(node->right->value)) { + cpu9900_node_label(node->left); + break; + } + if (node->type == N_LESSEQUAL8 || node->type == N_GREATER8) { + if (node->left->type == N_NUM8) { + cpu9900_node_label(node->right); + node->regs = node->right->regs | REG_1; + } else if (node->left->type == N_LOAD8) { + cpu9900_node_label(node->right); + node->regs = node->right->regs; + } else { + cpu9900_node_label(node->left); + cpu9900_node_label(node->right); + node->regs = node->left->regs | node->right->regs | REG_1; + } + } else if (node->type != N_XOR8 && node->type != N_AND8 && node->right->type == N_LOAD8) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + } else if (node->right->type == N_NUM8) { + cpu9900_node_label(node->left); + node->regs = node->left->regs | REG_1; + } else { + cpu9900_node_label(node->left); + cpu9900_node_label(node->right); + node->regs = node->left->regs | node->right->regs | REG_1; + } + break; + case N_ASSIGN8: /* 8-bit assignment */ + if (node->right->type == N_ADDR) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + break; + } + if ((node->right->type == N_PLUS16 || node->right->type == N_MINUS16) && node->right->left->type == N_ADDR && node->right->right->type == N_NUM16) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + break; + } + cpu9900_node_label(node->left); + cpu9900_node_label(node->right); + node->regs = node->left->regs | node->right->regs | REG_1; + break; + case N_ASSIGN16: /* 16-bit assignment */ + if (node->right->type == N_ADDR) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + break; + } + if ((node->right->type == N_PLUS16 || node->right->type == N_MINUS16) && node->right->left->type == N_ADDR && node->right->right->type == N_NUM16) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + break; + } + cpu9900_node_label(node->left); + cpu9900_node_label(node->right); + node->regs = node->left->regs | node->right->regs | REG_1; + break; + default: /* Every other node, all remaining are 16-bit operations */ + /* Optimization of address plus/minus constant */ + if (node->type == N_PLUS16 || node->type == N_MINUS16) { + if (node->left->type == N_ADDR) { + if (node->right->type == N_NUM16) { + node->regs = REG_0; + break; + } + } + } + if (node->type == N_PLUS16) { + if (node->left->type == N_ADDR) { + cpu9900_node_label(node->right); + node->regs = node->right->regs; + break; + } + if (node->left->type == N_NUM16 || node->right->type == N_NUM16) { + if (node->left->type == N_NUM16) + explore = node->left; + else + explore = node->right; + if (node->left != explore) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + } else { + cpu9900_node_label(node->right); + node->regs = node->right->regs; + } + break; + } + } + if (node->type == N_EQUAL16 || node->type == N_NOTEQUAL16) { + if (node->left->type == N_NUM16 || node->right->type == N_NUM16) { + if (node->left->type == N_NUM16) + explore = node->left; + else + explore = node->right; + if (node->left != explore) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + } else { + cpu9900_node_label(node->right); + node->regs = node->right->regs; + } + break; + } + } + if (node->type == N_LESS16 || node->type == N_GREATER16) { + if (node->right->type == N_NUM16) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + break; + } + } + if (node->type == N_MINUS16) { + if (node->right->type == N_NUM16) + explore = node->right; + else + explore = NULL; + if (explore != NULL) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + break; + } + } + if (node->type == N_OR16 || node->type == N_AND16 || node->type == N_XOR16) { + if (node->right->type == N_NUM16) + explore = node->right; + else + explore = NULL; + if (explore != NULL) { + int value = explore->value; + + if (node->left != explore) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + } else { + cpu9900_node_label(node->right); + node->regs = node->right->regs; + } + if ((node->type == N_OR16 || node->type == N_XOR16) && value == 0x00) { + } else if (node->type == N_AND16 && value == 0xffff) { + } else if (node->type == N_AND16 && value == 0x0000) { + } else if (node->type == N_OR16 && value == 0xffff) { + } else { + if (node->type == N_XOR16) + node->regs |= REG_1; + } + break; + } + } + if (node->type == N_MUL16) { + if (node->left->type == N_NUM16) + explore = node->left; + else if (node->right->type == N_NUM16) + explore = node->right; + else + explore = NULL; + if (explore != NULL && (explore->value == 0 || explore->value == 1 || is_power_of_two(explore->value))) { + int c = explore->value; + + if (c == 0) { + node->regs = REG_0; + } else { + if (node->left != explore) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + } else { + cpu9900_node_label(node->right); + node->regs = node->right->regs; + } + } + break; + } + } + if (node->type == N_DIV16) { + if (node->right->type == N_NUM16 && is_power_of_two(node->right->value)) { + cpu9900_node_label(node->left); + node->regs = node->left->regs; + break; + } + } + if (node->type == N_LESSEQUAL16 || node->type == N_GREATER16) { + if (node->left->type == N_LOAD16) { + cpu9900_node_label(node->right); + node->regs = node->right->regs; + } else { + cpu9900_node_label(node->left); + cpu9900_node_label(node->right); + node->regs = node->left->regs | node->right->regs | REG_1; + } + } else if (node->type != N_AND16 && node->right->type == N_LOAD16) { + /* + ** Cannot be optimized: + ** o AND requires to invert the source operand (i.e alter the variable) + */ + cpu9900_node_label(node->left); + node->regs = node->left->regs; + } else { + cpu9900_node_label(node->left); + cpu9900_node_label(node->right); + node->regs = node->left->regs | node->right->regs | REG_1; + } + if (node->type == N_MUL16) { + node->regs |= REG_1; /* r1 * r0 => r0_r1 (32 bit) */ + } else if (node->type == N_DIV16) { + node->regs |= REG_1 | REG_2; /* r0_r1 / r2 => r0, rem r1 */ + } else if (node->type == N_MOD16) { + node->regs |= REG_1 | REG_2; /* r0_r1 / r2 => r0, rem r1 */ + } else if (node->type == N_DIV16S) { + node->regs |= REG_1 | REG_2 | REG_3; + } else if (node->type == N_MOD16S) { + node->regs |= REG_1 | REG_2; + } + break; + } } /* @@ -769,6 +1064,24 @@ void cpu9900_node_generate(struct node *node, int decision) case N_MINUS8: /* 8-bit - */ case N_MUL8: /* 8-bit * */ case N_DIV8: /* 8-bit / */ + if (node->type == N_PLUS8 && node->right->type == N_NUM8) { + int value; + + cpu9900_node_generate(node->left, 0); + value = node->right->value; + sprintf(temp, "%d\t; %d", value * 256, value); + cpu9900_2op("ai", "r0", temp); + break; + } + if (node->type == N_MINUS8 && node->right->type == N_NUM8) { + int value; + + cpu9900_node_generate(node->left, 0); + value = -node->right->value & 0xff; + sprintf(temp, "%d\t; -%d", value * 256, node->right->value); + cpu9900_2op("ai", "r0", temp); + break; + } if (node->type == N_MUL8 && node->right->type == N_NUM8 && is_power_of_two(node->right->value)) { int c,cnt; @@ -814,10 +1127,15 @@ void cpu9900_node_generate(struct node *node, int decision) node_get_label(node->left, ADDRESS); } else { cpu9900_node_generate(node->left, 0); - cpu9900_1op("dect", "r10"); - cpu9900_2op("mov", "r0", "*r10"); - cpu9900_node_generate(node->right, 0); - cpu9900_2op("mov", "*r10+", "r1"); + if ((node->right->regs & REG_1) == 0) { + cpu9900_2op("mov", "r0", "r1"); + cpu9900_node_generate(node->right, 0); + } else { + cpu9900_1op("dect", "r10"); + cpu9900_2op("mov", "r0", "*r10"); + cpu9900_node_generate(node->right, 0); + cpu9900_2op("mov", "*r10+", "r1"); + } strcpy(temp, "r1"); } } else if (node->type != N_XOR8 && node->type != N_AND8 && node->right->type == N_LOAD8) { @@ -838,10 +1156,15 @@ void cpu9900_node_generate(struct node *node, int decision) strcpy(temp, "r1"); } else { cpu9900_node_generate(node->right, 0); - cpu9900_1op("dect", "r10"); - cpu9900_2op("mov", "r0", "*r10"); - cpu9900_node_generate(node->left, 0); - cpu9900_2op("mov", "*r10+", "r1"); + if ((node->left->regs & REG_1) == 0) { + cpu9900_2op("mov", "r0", "r1"); + cpu9900_node_generate(node->left, 0); + } else { + cpu9900_1op("dect", "r10"); + cpu9900_2op("mov", "r0", "*r10"); + cpu9900_node_generate(node->left, 0); + cpu9900_2op("mov", "*r10+", "r1"); + } strcpy(temp, "r1"); } if (node->type == N_OR8) { @@ -886,12 +1209,13 @@ void cpu9900_node_generate(struct node *node, int decision) cpu9900_label(temp + 100); } else { sprintf(temp, INTERNAL_PREFIX "%d", next_local++); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); cpu9900_1op("jeq", temp); cpu9900_1op("clr", "r0"); - cpu9900_1op("jmp", "$+4"); + cpu9900_1op("jmp", temp + 100); cpu9900_label(temp); cpu9900_1op("seto", "r0"); - cpu9900_empty(); + cpu9900_label(temp + 100); } } else if (node->type == N_NOTEQUAL8) { cpu9900_2op("cb", temp, "r0"); @@ -904,12 +1228,13 @@ void cpu9900_node_generate(struct node *node, int decision) cpu9900_label(temp + 100); } else { sprintf(temp, INTERNAL_PREFIX "%d", next_local++); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); cpu9900_1op("jne", temp); cpu9900_1op("clr", "r0"); - cpu9900_1op("jmp", "$+4"); + cpu9900_1op("jmp", temp + 100); cpu9900_label(temp); cpu9900_1op("seto", "r0"); - cpu9900_empty(); + cpu9900_label(temp + 100); } } else if (node->type == N_LESS8 || node->type == N_GREATER8) { cpu9900_2op("cb", "r0", temp); @@ -922,12 +1247,13 @@ void cpu9900_node_generate(struct node *node, int decision) cpu9900_label(temp + 100); } else { sprintf(temp, INTERNAL_PREFIX "%d", next_local++); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); cpu9900_1op("jl", temp); cpu9900_1op("clr", "r0"); - cpu9900_1op("jmp", "$+4"); + cpu9900_1op("jmp", temp + 100); cpu9900_label(temp); cpu9900_1op("seto", "r0"); - cpu9900_empty(); + cpu9900_label(temp + 100); } } else if (node->type == N_LESSEQUAL8 || node->type == N_GREATEREQUAL8) { cpu9900_2op("cb", "r0", temp); @@ -940,12 +1266,13 @@ void cpu9900_node_generate(struct node *node, int decision) cpu9900_label(temp + 100); } else { sprintf(temp, INTERNAL_PREFIX "%d", next_local++); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); cpu9900_1op("jhe", temp); cpu9900_1op("clr", "r0"); - cpu9900_1op("jmp", "$+4"); + cpu9900_1op("jmp", temp + 100); /* Cannot be $+4 */ cpu9900_label(temp); cpu9900_1op("seto", "r0"); - cpu9900_empty(); + cpu9900_label(temp + 100); /* To avoid optimizing seto r0 -> r1 */ } } else if (node->type == N_PLUS8) { cpu9900_2op("ab", temp, "r0"); @@ -977,10 +1304,15 @@ void cpu9900_node_generate(struct node *node, int decision) break; } cpu9900_node_generate(node->left, 0); - cpu9900_1op("dect", "r10"); - cpu9900_2op("mov", "r0", "*r10"); - cpu9900_node_generate(node->right, 0); - cpu9900_2op("mov", "*r10+", "r1"); + if ((node->right->regs & REG_1) == 0) { + cpu9900_2op("mov", "r0", "r1"); + cpu9900_node_generate(node->right, 0); + } else { + cpu9900_1op("dect", "r10"); + cpu9900_2op("mov", "r0", "*r10"); + cpu9900_node_generate(node->right, 0); + cpu9900_2op("mov", "*r10+", "r1"); + } cpu9900_2op("movb", "r1", "*r0"); break; case N_ASSIGN16: /* 16-bit assignment */ @@ -1007,10 +1339,15 @@ void cpu9900_node_generate(struct node *node, int decision) break; } cpu9900_node_generate(node->left, 0); - cpu9900_1op("dect", "r10"); - cpu9900_2op("mov", "r0", "*r10"); - cpu9900_node_generate(node->right, 0); - cpu9900_2op("mov", "*r10+", "r1"); + if ((node->right->regs & REG_1) == 0) { + cpu9900_2op("mov", "r0", "r1"); + cpu9900_node_generate(node->right, 0); + } else { + cpu9900_1op("dect", "r10"); + cpu9900_2op("mov", "r0", "*r10"); + cpu9900_node_generate(node->right, 0); + cpu9900_2op("mov", "*r10+", "r1"); + } cpu9900_2op("mov", "r1", "*r0"); break; default: /* Every other node, all remaining are 16-bit operations */ @@ -1058,6 +1395,75 @@ void cpu9900_node_generate(struct node *node, int decision) break; } } + if (node->type == N_EQUAL16 || node->type == N_NOTEQUAL16) { + if (node->left->type == N_NUM16 || node->right->type == N_NUM16) { + int c; + + if (node->left->type == N_NUM16) + explore = node->left; + else + explore = node->right; + if (node->left != explore) + cpu9900_node_generate(node->left, 0); + else + cpu9900_node_generate(node->right, 0); + c = explore->value; + sprintf(temp, "%d", c); + cpu9900_2op("ci", "r0", temp); + if (decision) { + optimized = 1; + sprintf(temp, "@" INTERNAL_PREFIX "%d", decision); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); + cpu9900_1op((node->type == N_EQUAL16 ? "jeq" : "jne"), temp + 100); + cpu9900_1op("b", temp); + cpu9900_label(temp + 100); + } else { + sprintf(temp, INTERNAL_PREFIX "%d", next_local++); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); + cpu9900_1op((node->type == N_EQUAL16 ? "jeq" : "jne"), temp); + cpu9900_1op("clr", "r0"); + cpu9900_1op("jmp", temp + 100); + cpu9900_label(temp); + cpu9900_1op("seto", "r0"); + cpu9900_label(temp + 100); + } + break; + } + } + if (node->type == N_LESS16 || node->type == N_GREATER16) { + if (node->right->type == N_NUM16) { + int c; + + cpu9900_node_generate(node->left, 0); + c = node->right->value; + sprintf(temp, "%d", c); + cpu9900_2op("ci", "r0", temp); + if (decision) { + optimized = 1; + sprintf(temp, "@" INTERNAL_PREFIX "%d", decision); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); + if (node->type == N_LESS16) + cpu9900_1op("jl", temp + 100); + else if (node->type == N_GREATER16) + cpu9900_1op("jh", temp + 100); + cpu9900_1op("b", temp); + cpu9900_label(temp + 100); + } else { + sprintf(temp, INTERNAL_PREFIX "%d", next_local++); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); + if (node->type == N_LESS16) + cpu9900_1op("jl", temp); + else if (node->type == N_GREATER16) + cpu9900_1op("jh", temp); + cpu9900_1op("clr", "r0"); + cpu9900_1op("jmp", temp + 100); + cpu9900_label(temp); + cpu9900_1op("seto", "r0"); + cpu9900_label(temp + 100); + } + break; + } + } if (node->type == N_MINUS16) { if (node->right->type == N_NUM16) explore = node->right; @@ -1130,7 +1536,7 @@ void cpu9900_node_generate(struct node *node, int decision) if (c == 0) { cpu9900_1op("clr", "r0"); } else if (c == 1) { - // nothing to do + cpu9900_node_generate(node, 0); } else { int cnt; @@ -1169,10 +1575,15 @@ void cpu9900_node_generate(struct node *node, int decision) node_get_label(node->left, ADDRESS); } else { cpu9900_node_generate(node->left, 0); - cpu9900_1op("dect", "r10"); - cpu9900_2op("mov", "r0", "*r10"); - cpu9900_node_generate(node->right, 0); - cpu9900_2op("mov", "*r10+", "r1"); + if ((node->right->regs & REG_1) == 0) { + cpu9900_2op("mov", "r0", "r1"); + cpu9900_node_generate(node->right, 0); + } else { + cpu9900_1op("dect", "r10"); + cpu9900_2op("mov", "r0", "*r10"); + cpu9900_node_generate(node->right, 0); + cpu9900_2op("mov", "*r10+", "r1"); + } strcpy(temp, "r1"); } } else if (node->type != N_AND16 && node->right->type == N_LOAD16) { @@ -1183,11 +1594,22 @@ void cpu9900_node_generate(struct node *node, int decision) cpu9900_node_generate(node->left, 0); node_get_label(node->right, ADDRESS); } else { - cpu9900_node_generate(node->right, 0); - cpu9900_1op("dect", "r10"); - cpu9900_2op("mov", "r0", "*r10"); - cpu9900_node_generate(node->left, 0); - cpu9900_2op("mov", "*r10+", "r1"); + if (node->right->type == N_NUM16) { + cpu9900_node_generate(node->left, 0); + sprintf(temp, "%d", node->right->value); + cpu9900_2op("li", "r1", temp); + } else { + cpu9900_node_generate(node->right, 0); + if ((node->left->regs & REG_1) == 0) { + cpu9900_2op("mov", "r0", "r1"); + cpu9900_node_generate(node->left, 0); + } else { + cpu9900_1op("dect", "r10"); + cpu9900_2op("mov", "r0", "*r10"); + cpu9900_node_generate(node->left, 0); + cpu9900_2op("mov", "*r10+", "r1"); + } + } strcpy(temp, "r1"); } if (node->type == N_OR16) { @@ -1232,12 +1654,13 @@ void cpu9900_node_generate(struct node *node, int decision) cpu9900_label(temp + 100); } else { sprintf(temp, INTERNAL_PREFIX "%d", next_local++); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); cpu9900_1op("jeq", temp); cpu9900_1op("clr", "r0"); - cpu9900_1op("jmp", "$+4"); + cpu9900_1op("jmp", temp + 100); cpu9900_label(temp); cpu9900_1op("seto", "r0"); - cpu9900_empty(); + cpu9900_label(temp + 100); } } else if (node->type == N_NOTEQUAL16) { cpu9900_2op("c", "r0", temp); @@ -1250,12 +1673,13 @@ void cpu9900_node_generate(struct node *node, int decision) cpu9900_label(temp + 100); } else { sprintf(temp, INTERNAL_PREFIX "%d", next_local++); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); cpu9900_1op("jne", temp); cpu9900_1op("clr", "r0"); - cpu9900_1op("jmp", "$+4"); + cpu9900_1op("jmp", temp + 100); cpu9900_label(temp); cpu9900_1op("seto", "r0"); - cpu9900_empty(); + cpu9900_label(temp + 100); } } else if (node->type == N_LESS16 || node->type == N_GREATER16) { cpu9900_2op("c", "r0", temp); @@ -1268,12 +1692,13 @@ void cpu9900_node_generate(struct node *node, int decision) cpu9900_label(temp + 100); } else { sprintf(temp, INTERNAL_PREFIX "%d", next_local++); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); cpu9900_1op("jl", temp); cpu9900_1op("clr", "r0"); - cpu9900_1op("jmp", "$+4"); + cpu9900_1op("jmp", temp + 100); cpu9900_label(temp); cpu9900_1op("seto", "r0"); - cpu9900_empty(); + cpu9900_label(temp + 100); } } else if (node->type == N_LESSEQUAL16 || node->type == N_GREATEREQUAL16) { cpu9900_2op("c", "r0", temp); @@ -1286,12 +1711,13 @@ void cpu9900_node_generate(struct node *node, int decision) cpu9900_label(temp + 100); } else { sprintf(temp, INTERNAL_PREFIX "%d", next_local++); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); cpu9900_1op("jhe", temp); cpu9900_1op("clr", "r0"); - cpu9900_1op("jmp", "$+4"); + cpu9900_1op("jmp", temp + 100); cpu9900_label(temp); cpu9900_1op("seto", "r0"); - cpu9900_empty(); + cpu9900_label(temp + 100); } } else if (node->type == N_PLUS16) { cpu9900_2op("a", temp, "r0"); diff --git a/cpu9900.h b/cpu9900.h index cb1f703..fe73b0c 100644 --- a/cpu9900.h +++ b/cpu9900.h @@ -6,6 +6,15 @@ ** Creation date: Aug/20/2024. Separated from CVBasic.c */ +#define REG_0 0x01 +#define REG_1 0x02 +#define REG_2 0x04 +#define REG_3 0x08 +#define REG_4 0x10 +#define REG_5 0x20 +#define REG_6 0x40 +#define REG_7 0x80 + extern void cpu9900_dump(void); extern void cpu9900_label(char *); extern void cpu9900_empty(void); diff --git a/cpuz80.c b/cpuz80.c index ada172a..63ff143 100644 --- a/cpuz80.c +++ b/cpuz80.c @@ -12,6 +12,15 @@ #include #include "cvbasic.h" #include "node.h" +#include "cpuz80.h" + +#define REG_ALL (REG_AF | REG_BC | REG_DE | REG_HL) + +/* + ** Character used internally to mark precalculated array[index] addresses + */ +#define INDEX_ADD '[' +#define INDEX_ADD_STRING "[" static char z80_line[MAX_LINE_SIZE]; @@ -19,16 +28,16 @@ static char z80_line_1[MAX_LINE_SIZE]; static char z80_line_2[MAX_LINE_SIZE]; static char z80_line_3[MAX_LINE_SIZE]; -static char z80_a_content[MAX_LINE_SIZE]; -static char z80_hl_content[MAX_LINE_SIZE]; +static char z80_a_value[MAX_LINE_SIZE]; +static char z80_a_alias[MAX_LINE_SIZE]; +static char z80_hl_value[MAX_LINE_SIZE]; +static char z80_hl_alias[MAX_LINE_SIZE]; static int z80_flag_z_valid; static void z80_emit_line(void); void z80_emit_line(void) { - int c; - int d; /* ** Optimize the following cases: @@ -92,8 +101,10 @@ void cpuz80_label(char *label) { sprintf(z80_line, "%s:\n", label); z80_emit_line(); - z80_a_content[0] = '\0'; - z80_hl_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; + z80_hl_value[0] = '\0'; + z80_hl_alias[0] = '\0'; z80_flag_z_valid = 0; } @@ -102,7 +113,8 @@ void cpuz80_label(char *label) */ void cpuz80_empty(void) { - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; z80_flag_z_valid = 0; } @@ -113,7 +125,8 @@ void cpuz80_noop(char *mnemonic) { sprintf(z80_line, "\t%s\n", mnemonic); z80_emit_line(); - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; if (strcmp(mnemonic, "NEG") == 0) z80_flag_z_valid = 1; else @@ -130,7 +143,7 @@ void cpuz80_1op(char *mnemonic, char *operand) */ if (strcmp(mnemonic, "SUB") == 0) { if (strcmp(operand, "A") == 0) { - if (strcmp(z80_a_content, "0") == 0) + if (strcmp(z80_a_value, "0") == 0) return; } } @@ -158,61 +171,82 @@ void cpuz80_1op(char *mnemonic, char *operand) z80_flag_z_valid = 0; } else if (strcmp(mnemonic, "POP") == 0) { if (strcmp(operand, "AF") == 0) { - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; z80_flag_z_valid = 0; } else if (strcmp(operand, "HL") == 0) { - z80_hl_content[0] = '\0'; + z80_hl_value[0] = '\0'; + z80_hl_alias[0] = '\0'; } } else if (strcmp(mnemonic, "CALL") == 0 || - strcmp(mnemonic, "JP") == 0) { - z80_a_content[0] = '\0'; - z80_hl_content[0] = '\0'; + strcmp(mnemonic, "JP") == 0 || + strcmp(mnemonic, "JR") == 0) { + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; + z80_hl_value[0] = '\0'; + z80_hl_alias[0] = '\0'; z80_flag_z_valid = 0; } else if (strcmp(mnemonic, "SUB") == 0) { if (strcmp(operand, "A") == 0) - strcpy(z80_a_content, "0"); + strcpy(z80_a_value, "0"); else - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; z80_flag_z_valid = 1; } else if (strcmp(mnemonic, "OR") == 0 || strcmp(mnemonic, "XOR") == 0 || strcmp(mnemonic, "AND") == 0) { - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; z80_flag_z_valid = 1; } else if (strcmp(mnemonic, "SRL") == 0) { - if (strcmp(operand, "H") == 0) - z80_hl_content[0] = '\0'; - else if (strcmp(operand, "A") == 0) + if (strcmp(operand, "H") == 0) { + z80_hl_value[0] = '\0'; + z80_hl_alias[0] = '\0'; + } else if (strcmp(operand, "A") == 0) { z80_flag_z_valid = 1; + } } else if (strcmp(mnemonic, "RR") == 0) { - if (strcmp(operand, "L") == 0) - z80_hl_content[0] = '\0'; + if (strcmp(operand, "L") == 0) { + z80_hl_value[0] = '\0'; + z80_hl_alias[0] = '\0'; + } z80_flag_z_valid = 0; } else if (strcmp(mnemonic, "INC") == 0) { if (strcmp(operand, "H") == 0 || strcmp(operand, "L") == 0 || strcmp(operand, "HL") == 0) { - z80_hl_content[0] = '\0'; + z80_hl_value[0] = '\0'; + z80_hl_alias[0] = '\0'; z80_flag_z_valid = 0; } else if (strcmp(operand, "A") == 0) { - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; z80_flag_z_valid = 1; } else if (strcmp(operand, "(HL)") == 0) { - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; z80_flag_z_valid = 0; + if (strchr(z80_hl_alias, INDEX_ADD) != NULL) + z80_hl_alias[0] = '\0'; } } else if (strcmp(mnemonic, "DEC") == 0) { if (strcmp(operand, "H") == 0 || strcmp(operand, "L") == 0 || strcmp(operand, "HL") == 0) { - z80_hl_content[0] = '\0'; + z80_hl_value[0] = '\0'; + z80_hl_alias[0] = '\0'; z80_flag_z_valid = 0; } else if (strcmp(operand, "A") == 0) { - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; z80_flag_z_valid = 1; } else if (strcmp(operand, "(HL)") == 0) { - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; z80_flag_z_valid = 0; + if (strchr(z80_hl_alias, INDEX_ADD) != NULL) + z80_hl_alias[0] = '\0'; } } else if (strcmp(mnemonic, "DW") == 0 || strcmp(mnemonic, "ORG") == 0 || strcmp(mnemonic, "FORG") == 0) { /* Nothing to do */ @@ -234,26 +268,28 @@ void cpuz80_2op(char *mnemonic, char *operand1, char *operand2) special = 0; if (strcmp(mnemonic, "LD") == 0) { if (strcmp(operand1, "A") == 0) { - if (strcmp(operand2, z80_a_content) == 0) + if (strcmp(operand2, z80_a_alias) == 0 || strcmp(operand2, z80_a_value) == 0) return; - if (strcmp(operand2, z80_hl_content) == 0) { + if (strcmp(operand2, z80_hl_alias) == 0 || strcmp(operand2, z80_hl_value) == 0) { operand2 = "L"; /* Reading from memory address, and HL already has the address */ - } else if (operand2[0] == '(' && operand2[strlen(operand2) - 1] == ')' && memcmp(&operand2[1], z80_hl_content, strlen(operand2) - 2) == 0 && z80_hl_content[strlen(operand2) - 2] == '\0') { + } else if (operand2[0] == '(' && operand2[strlen(operand2) - 1] == ')' && memcmp(&operand2[1], z80_hl_alias, strlen(operand2) - 2) == 0 && z80_hl_alias[strlen(operand2) - 2] == '\0') { /* Generate subexpression info and mark as previously processed */ z80_flag_z_valid = 0; - strcpy(z80_a_content, operand2); + z80_a_value[0] = '\0'; + strcpy(z80_a_alias, operand2); special = 1; /* Mark as processed */ operand2 = "(HL)"; } } else if (strcmp(operand1, "HL") == 0) { - if (strcmp(operand2, z80_hl_content) == 0) + if (strcmp(operand2, z80_hl_alias) == 0 || strcmp(operand2, z80_hl_value) == 0) return; } else if (strcmp(operand2, "A") == 0) { /* Writing to memory address, and HL already has the address */ - if (operand1[0] == '(' && operand1[strlen(operand1) - 1] == ')' && memcmp(&operand1[1], z80_hl_content, strlen(operand1) - 2) == 0 && z80_hl_content[strlen(operand1) - 2] == '\0') { + if (operand1[0] == '(' && operand1[strlen(operand1) - 1] == ')' && memcmp(&operand1[1], z80_hl_alias, strlen(operand1) - 2) == 0 && z80_hl_alias[strlen(operand1) - 2] == '\0') { /* Generate subexpression info and mark as previously processed */ - strcpy(z80_a_content, operand1); + strcpy(z80_a_alias, operand1); + /* It doesn't erase z80_a_value as any value is still valid */ special = 1; /* Mark as processed */ operand1 = "(HL)"; } @@ -270,16 +306,20 @@ void cpuz80_2op(char *mnemonic, char *operand1, char *operand2) strcmp(mnemonic, "SET") == 0) { /* No affected registers or flags */ } else if (strcmp(mnemonic, "EX") == 0) { - z80_hl_content[0] = '\0'; + z80_hl_value[0] = '\0'; + z80_hl_alias[0] = '\0'; } else if (strcmp(mnemonic, "IN") == 0) { - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; z80_flag_z_valid = 0; } else if (strcmp(mnemonic, "ADD") == 0 || strcmp(mnemonic, "ADC") == 0 || strcmp(mnemonic, "SBC") == 0) { if (strcmp(operand1, "A") == 0) { - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; z80_flag_z_valid = 1; } else { - z80_hl_content[0] = '\0'; + z80_hl_value[0] = '\0'; + z80_hl_alias[0] = '\0'; z80_flag_z_valid = 0; } } else if (strcmp(mnemonic, "LD") == 0) { @@ -287,23 +327,47 @@ void cpuz80_2op(char *mnemonic, char *operand1, char *operand2) return; if (strcmp(operand1, "A") == 0) /* Read value into accumulator */ z80_flag_z_valid = 0; /* Z status isn't valid */ - if (strcmp(operand1, "L") == 0 || strcmp(operand1, "H") == 0) - z80_hl_content[0] = '\0'; - if (strcmp(operand1, "HL") == 0) - strcpy(z80_hl_content, operand2); - else if (strcmp(operand2, "HL") == 0) - strcpy(z80_hl_content, operand1); + if (strcmp(operand1, "L") == 0 || strcmp(operand1, "H") == 0) { + z80_hl_value[0] = '\0'; + z80_hl_alias[0] = '\0'; + } + if (strcmp(operand1, "HL") == 0) { + if (isdigit(operand2[0])) { + /* Loading a value destroys any previous alias */ + strcpy(z80_hl_value, operand2); + z80_hl_alias[0] = '\0'; + } else { + /* Loading from an address destroy any previous value */ + strcpy(z80_hl_alias, operand2); + z80_hl_value[0] = '\0'; + } + } else if (strcmp(operand2, "HL") == 0) { + /* Saving to an address makes HL an alias AND PRESERVES the value */ + strcpy(z80_hl_alias, operand1); + } if (strcmp(operand1, "A") == 0 && strcmp(operand2, "(HL)") == 0) { - z80_a_content[0] = '\0'; + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; } else if (strcmp(operand1, "(HL)") == 0 && strcmp(operand2, "A") == 0) { /* A keeps its value */ } else if (strcmp(operand1, "A") == 0) { - if (isdigit(operand2[0]) || operand2[0] == '(') - strcpy(z80_a_content, operand2); - else - z80_a_content[0] = '\0'; - } else if (strcmp(operand2, "A") == 0 && operand1[0] == '(') { - strcpy(z80_a_content, operand1); + if (isdigit(operand2[0])) { + /* Loading a value destroys any previous alias */ + strcpy(z80_a_value, operand2); + z80_a_alias[0] = '\0'; + } else if (operand2[0] == '(') { + /* Loading from an address destroy any previous value */ + z80_a_value[0] = '\0'; + strcpy(z80_a_alias, operand2); + } else { + z80_a_value[0] = '\0'; + z80_a_alias[0] = '\0'; + } + } else if (operand1[0] == '(' && strcmp(operand2, "A") == 0) { + /* Saving to an address makes A an alias AND PRESERVES the value */ + strcpy(z80_a_alias, operand1); + if (strchr(z80_hl_alias, INDEX_ADD) != NULL) + z80_hl_alias[0] = '\0'; } } else { fprintf(stderr, "z80_2op: not found mnemonic %s\n", mnemonic); @@ -392,6 +456,8 @@ void cpuz80_node_label(struct node *node) case N_JOY2: /* Read joystick 2 */ case N_KEY1: /* Read keypad 1 */ case N_KEY2: /* Read keypad 2 */ + case N_SPINNER1: /* Read spinner 1 */ + case N_SPINNER2: /* Read spinner 2 */ case N_MUSIC: /* Read music playing status */ case N_NTSC: /* Read NTSC flag */ case N_VDPSTATUS: /* Read VDP status */ @@ -500,7 +566,9 @@ void cpuz80_node_label(struct node *node) } else { cpuz80_node_label(node->right); cpuz80_node_label(node->left); - if (node->left->regs == REG_A) { + if ((node->type == N_PLUS8 || node->type == N_MINUS8 || node->type == N_OR8 || node->type == N_AND8 || node->type == N_XOR8) && node->right->type == N_PEEK8 && (node->right->left->regs & REG_A) == 0) { + node->regs = node->left->regs | node->right->left->regs; + } else if (node->left->regs == REG_A) { node->regs = REG_A | REG_B; } else { node->regs = node->left->regs | node->right->regs | REG_BC; @@ -916,6 +984,24 @@ void cpuz80_node_generate(struct node *node, int decision) case N_KEY2: /* Read keypad 2 */ cpuz80_2op("LD", "A", "(key2_data)"); break; + case N_SPINNER1: /* Read spinner 1 */ + cpuz80_noop("DI"); + cpuz80_2op("LD", "A", "(spinner_data+1)"); + cpuz80_1op("PUSH", "AF"); + cpuz80_1op("XOR", "A"); + cpuz80_2op("LD", "(spinner_data+1)", "A"); + cpuz80_1op("POP", "AF"); + cpuz80_noop("EI"); + break; + case N_SPINNER2: /* Read spinner 2 */ + cpuz80_noop("DI"); + cpuz80_2op("LD", "A", "(spinner_data+2)"); + cpuz80_1op("PUSH", "AF"); + cpuz80_1op("XOR", "A"); + cpuz80_2op("LD", "(spinner_data+2)", "A"); + cpuz80_1op("POP", "AF"); + cpuz80_noop("EI"); + break; case N_RANDOM: /* Read pseudorandom generator */ cpuz80_1op("CALL", "random"); break; @@ -923,7 +1009,14 @@ void cpuz80_node_generate(struct node *node, int decision) cpuz80_2op("LD", "HL", "(frame)"); break; case N_MUSIC: /* Read music playing status */ - cpuz80_2op("LD", "A", "(music_playing)"); + if (node->value == 0) { + cpuz80_2op("LD", "A", "(music_playing)"); + } else if (node->value == 1) { + cpuz80_2op("LD", "A", "(fm_slot)"); + cpuz80_1op("INC", "A"); + } else if (node->value == 2) { + cpuz80_2op("LD", "A", "(fm_enabled)"); + } break; case N_NTSC: /* Read NTSC flag */ cpuz80_2op("LD", "A", "(ntsc)"); @@ -1027,6 +1120,10 @@ void cpuz80_node_generate(struct node *node, int decision) break; } sprintf(temp, "%d", c); + } else if ((node->type == N_PLUS8 || node->type == N_MINUS8 || node->type == N_OR8 || node->type == N_AND8 || node->type == N_XOR8) && node->right->type == N_PEEK8 && (node->right->left->regs & REG_A) == 0) { + cpuz80_node_generate(node->left, 0); + cpuz80_node_generate(node->right->left, 0); + strcpy(temp, "(HL)"); } else { if ((node->left->regs & REG_B) == 0) { cpuz80_node_generate(node->right, 0); @@ -1296,8 +1393,8 @@ void cpuz80_node_generate(struct node *node, int decision) } /* Optimization of address plus/minus constant */ if (node->type == N_PLUS16 || node->type == N_MINUS16) { - if (node->left->type == N_ADDR) { - if (node->right->type == N_NUM16) { + if (node->left->type == N_ADDR) { /* addr + : addr - */ + if (node->right->type == N_NUM16) { /* addr + const : addr - const */ node_get_label(node->left, 0); if (node->type == N_PLUS16) strcat(temp, "+"); @@ -1314,10 +1411,45 @@ void cpuz80_node_generate(struct node *node, int decision) } if (node->type == N_PLUS16) { if (node->left->type == N_ADDR) { + if (node->right->type == N_EXTEND8 && node->right->left->type == N_LOAD8) { /* addr + var : addr - var */ + char *p; + + if ((p = strchr(z80_hl_alias, INDEX_ADD)) != NULL) { /* If there is an index saved... */ + node_get_label(node->right->left, 0); + if (strcmp(temp, p + 1) == 0) { /* ...and it is the same index */ + *p = '\0'; + node_get_label(node->left, 0); + if (strcmp(z80_hl_alias, temp) != 0) { /* Add difference between the two arrays position, or none if same array */ + strcat(temp, "-"); + strcat(temp, z80_hl_alias); + cpuz80_2op("LD", "DE", temp); + cpuz80_2op("ADD", "HL", "DE"); + } + node_get_label(node->left, 0); + strcpy(z80_hl_alias, temp); + strcat(z80_hl_alias, INDEX_ADD_STRING); + node_get_label(node->right->left, 0); + strcat(z80_hl_alias, temp); + break; + } + } + } cpuz80_node_generate(node->right, 0); node_get_label(node->left, 0); cpuz80_2op("LD", "DE", temp); cpuz80_2op("ADD", "HL", "DE"); + + /* + ** Higher-level optimization + ** z80_hl_alias indicates it contains address for array[var] + */ + if (node->right->type == N_EXTEND8 && node->right->left->type == N_LOAD8) { /* addr + var : addr - var */ + node_get_label(node->left, 0); + strcpy(z80_hl_alias, temp); + strcat(z80_hl_alias, INDEX_ADD_STRING); + node_get_label(node->right->left, 0); + strcat(z80_hl_alias, temp); + } break; } if (node->left->type == N_NUM16 || node->right->type == N_NUM16) { @@ -1498,9 +1630,17 @@ void cpuz80_node_generate(struct node *node, int decision) } else { cpuz80_node_generate(node, 0); } - while (c > 1) { - cpuz80_2op("ADD", "HL", "HL"); - c /= 2; + if (c == 128) { + cpuz80_1op("SRL", "H"); + cpuz80_2op("LD", "H", "L"); + cpuz80_1op("RR", "H"); + cpuz80_2op("LD", "L", "0"); + cpuz80_1op("RR", "L"); + } else { + while (c > 1) { + cpuz80_2op("ADD", "HL", "HL"); + c /= 2; + } } } break; @@ -1556,6 +1696,7 @@ void cpuz80_node_generate(struct node *node, int decision) cpuz80_node_generate(node->left, 0); if ((node->right->regs & REG_DE) == 0) { cpuz80_2op("EX", "DE", "HL"); + cpuz80_node_generate(node->right, 0); } else { cpuz80_1op("PUSH", "HL"); cpuz80_node_generate(node->right, 0); diff --git a/cpuz80.h b/cpuz80.h index 71add89..64955fe 100644 --- a/cpuz80.h +++ b/cpuz80.h @@ -6,6 +6,20 @@ ** Creation date: Jul/31/2024. Separated from CVBasic.c */ +#define REG_A 0x01 +#define REG_F 0x02 +#define REG_B 0x04 +#define REG_C 0x08 +#define REG_D 0x10 +#define REG_E 0x20 +#define REG_H 0x40 +#define REG_L 0x80 + +#define REG_AF (REG_A | REG_F) +#define REG_BC (REG_B | REG_C) +#define REG_DE (REG_D | REG_E) +#define REG_HL (REG_H | REG_L) + extern void cpuz80_dump(void); extern void cpuz80_label(char *); extern void cpuz80_empty(void); diff --git a/cvbasic.c b/cvbasic.c index 0adafcb..a768963 100644 --- a/cvbasic.c +++ b/cvbasic.c @@ -3,7 +3,7 @@ ** ** by Oscar Toledo G. ** - ** © Copyright 2024 Óscar Toledo G. + ** © Copyright 2024-2025 Óscar Toledo G. ** https://nanochess.org/ ** ** Creation date: Feb/27/2024. @@ -11,6 +11,8 @@ ** Revision date: Feb/29/2024. Implemented controller support. Added arrays, SOUND, ** RESTORE/READ/DATA. Added small local optimization. ** Revision date: Aug/23/2024. Added TI-99/4A. + ** Revision date: Apr/26/2025. Added Sega Master System. + ** Revision date: Aug/23/2025. Added NES/Famicom. */ #include @@ -31,7 +33,7 @@ #define DEFAULT_ASM_LIBRARY_PATH "" #endif -#define VERSION "v0.8.0 Nov/12/2024" +#define VERSION "v0.9.2 Mar/12/2026" #define TEMPORARY_ASSEMBLER "cvbasic_temporary.asm" @@ -41,81 +43,71 @@ /* ** Supported platforms. */ -static enum { - COLECOVISION, - SG1000, - MSX, - COLECOVISION_SGM, - SVI, - SORD, - MEMOTECH, - CREATIVISION, - PENCIL, - EINSTEIN, - PV2000, - TI994A, - NABU, - TOTAL_TARGETS -} machine; +enum supported_machine machine; enum cpu_target target; /* ** Base information about each platform. + ** + ** Machine name (for command line), extra options, description, canonical name + ** base address of ram, base address of stack, available ram bytes + ** vdp port for writing, vdp port for reading, psg port, 1 it uses INT for VDP */ -static struct console { - char *name; /* Machine name */ - char *options; /* Options */ - char *description; /* Description (for usage guide) */ - char *canonical; /* Canonical name */ - int base_ram; /* Where the RAM starts */ - int stack; /* Where the stack will start */ - int memory_size; /* Memory available */ - int vdp_port_write; /* VDP port for writing */ - int vdp_port_read; /* VDP port for reading (needed for SVI-318/328, sigh) */ - int psg_port; /* PSG port (SN76489) */ - enum cpu_target target; -} consoles[TOTAL_TARGETS] = { +struct console consoles[TOTAL_TARGETS] = { /* RAM STACK Size VDP R VDP W PSG */ {"colecovision","", "Standard Colecovision (1K RAM)", - "Colecovision", - 0x7000, 0x7400, 0x0400, 0xbe, 0xbe, 0xff, CPU_Z80}, + "Colecovision", "COLECOVISION", + 0x7000, 0x7400, 0x0400, 0xbe, 0xbe, 0xff, 0, CPU_Z80}, {"sg1000", "", "Sega SG-1000/SC-3000 (1K RAM)", - "Sega SG-1000/SC-3000", - 0xc000, 0xc400, 0x0400, 0xbe, 0xbe, 0x7f, CPU_Z80}, - {"msx", "-ram16", "MSX (8K RAM), use -ram16 for 16K of RAM", - "MSX", - 0xe000, 0xf380, 0x1380, 0x98, 0x98, 0, CPU_Z80}, + "Sega SG-1000/SC-3000", "SG1000", + 0xc000, 0xc400, 0x0400, 0xbe, 0xbe, 0x7f, 1, CPU_Z80}, + {"msx", "-ram16", "MSX (8K RAM), use -ram16 for 16K of RAM,\n use -konami for Konami mapper instead of ASCII16", + "MSX", "MSX", + 0xe000, 0xf380, 0x1380, 0x98, 0x98, 0, 1, CPU_Z80}, {"sgm", "", "Colecovision with Opcode's Super Game Module", - "Colecovision with SGM", - 0x7c00, 0x8000, 0x5c00, 0xbe, 0xbe, 0xff, CPU_Z80}, /* Note: Real RAM at 0x2000 */ + "Colecovision with SGM", "SGM", + 0x7c00, 0x8000, 0x5c00, 0xbe, 0xbe, 0xff, 0, CPU_Z80}, /* Note: Real RAM at 0x2000 */ {"svi", "", "Spectravideo SVI-318/328 (16K of RAM)", - "Spectravideo SVI-318/328", - 0xc000, 0xf000, 0x3000, 0x80, 0x84, 0, CPU_Z80}, + "Spectravideo SVI-318/328", "SVI", + 0xc000, 0xf000, 0x3000, 0x80, 0x84, 0, 1, CPU_Z80}, {"sord", "", "Sord M5 (1K RAM)", - "Sord M5", - 0x7080, 0x7080, 0x0380, 0x10, 0x10, 0x20, CPU_Z80}, + "Sord M5", "SORD", + 0x7080, 0x7080, 0x0380, 0x10, 0x10, 0x20, 1, CPU_Z80}, {"memotech","-cpm", "Memotech MTX (64K RAM), generates .run files, use -cpm for .com files", - "Memotech MTX", - 0, 0xa000, 0, 0x01, 0x01, 0x06, CPU_Z80}, + "Memotech MTX", "MEMOTECH", + 0, 0xa000, 0, 0x01, 0x01, 0x06, 1, CPU_Z80}, {"creativision","-rom16","Vtech Creativision (Dick Smith's Wizzard/Laser 2001), 6502+1K RAM.", - "Creativision/Wizzard", - 0x0050, 0x017f, 0x0400, 0, 0, 0, CPU_6502}, + "Creativision/Wizzard", "CREATIVISION", + 0x0050, 0x017f, 0x0400, 0, 0, 0, 1, CPU_6502}, {"pencil", "", "Soundic/Hanimex Pencil II (2K RAM)", - "Soundic Pencil II", - 0x7000, 0x7800, 0x0800, 0xbe, 0xbe, 0xff, CPU_Z80}, + "Soundic Pencil II", "PENCIL", + 0x7000, 0x7800, 0x0800, 0xbe, 0xbe, 0xff, 0, CPU_Z80}, {"einstein","", "Tatung Einstein, generates .com files", - "Tatung Einstein", - 0, 0xa000, 0, 0x08, 0x08, 0, CPU_Z80}, + "Tatung Einstein", "EINSTEIN", + 0, 0xa000, 0, 0x08, 0x08, 0, 0, CPU_Z80}, {"pv2000", "", "Casio PV-2000", - "Casio PV-2000", - 0x7600, 0x8000, 0x0a00,0x4000, 0x4000, 0x40, CPU_Z80}, + "Casio PV-2000", "PV2000", + 0x7600, 0x8000, 0x0a00,0x4000, 0x4000, 0x40, 0, CPU_Z80}, {"ti994a", "", "Texas Instruments TI-99/4A (32K RAM). Support by tursilion", - "TI-99/4A (support by tursilion)", - 0x2080, 0x4000, 0x1f80, 0x8800, 0x8c00,0xff, CPU_9900}, + "TI-99/4A (support by tursilion)", "TI994A", + 0x2080, 0x4000, 0x1f80, 0x8800, 0x8c00,0xff, 1, CPU_9900}, {"nabu", "-cpm", "NABU PC (64K RAM)", - "Nabu PC", - 0, 0xe000, 0, 0xa0, 0xa0, 0, CPU_Z80}, + "Nabu PC", "NABU", + 0, 0xe000, 0, 0xa0, 0xa0, 0, 1, CPU_Z80}, + /* + ** For Sega Master System the stack cannot start at $e000, because + ** when using Bank Switching writing to $fffd-$ffff destroys $dffd-$dfff + */ + {"sms", "", "Sega Master System (8K RAM)", + "Sega Master System", "SMS", + 0xc000, 0xdff0, 0x1ff0, 0xbe, 0xbe, 0x7f, 1, CPU_Z80}, + {"nes", "", "NES/Famicom (2K RAM)", + "NES/Famicom", "NES", + 0x0050, 0x01ff, 0x0800, 0, 0, 0, 1, CPU_6502}, + {"msx2", "-ram16", "MSX2 (8K RAM), use -ram16 for 16K of RAM,\n use -konami for Konami mapper instead of ASCII16", + "MSX2", "MSX2", + 0xe000, 0xf380, 0x1380, 0x98, 0x98, 0, 1, CPU_Z80}, }; static int err_code; @@ -126,9 +118,11 @@ static char path[4096]; static int last_is_return; static int music_used; static int compression_used; +static int spinner_used; static int bank_switching; static int bank_rom_size; static int bank_current; +static int bank_konami; static char current_file[MAX_LINE_SIZE]; static int current_line; @@ -144,6 +138,7 @@ int next_local = 1; static int option_explicit; static int option_warnings; +static int option_fm; static enum lexical_component { C_END, C_NAME, @@ -217,6 +212,7 @@ struct node *process_usr(int); int optimized; +void check_for_macro(void); struct node *evaluate_level_0(int *); struct node *evaluate_level_1(int *); struct node *evaluate_level_2(int *); @@ -241,9 +237,140 @@ struct loop { static struct loop *loops; -static unsigned char bitmap[32]; +static unsigned char bitmap[128]; static int bitmap_byte; +/* + ** NES definitions + */ +#define CHRROM_PAGE 8192 + +static int current_chrrom; +static int chrrom_pointer; +static int chrrom_size; +static unsigned char *chrrom_data; +static int nes_nametable; + +void reset_chrrom(unsigned char *); + +void reset_chrrom(unsigned char *page) +{ + /* + ** Font for NES/Famicom, because it doesn't provide an ASCII charset. + ** + ** My personal font for TMS9928. + ** + ** Patterned after the TMS9928 programming manual 6x8 letters + ** with better lowercase letters, also I made a proper + ** AT sign. + */ + unsigned char font[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x20 space + 0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00, // 0x21 ! + 0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00, // 0x22 " + 0x50,0x50,0xf8,0x50,0xf8,0x50,0x50,0x00, // 0x23 # + 0x20,0x78,0xa0,0x70,0x28,0xf0,0x20,0x00, // 0x24 $ + 0xc0,0xc8,0x10,0x20,0x40,0x98,0x18,0x00, // 0x25 % + 0x40,0xa0,0x40,0xa0,0xa8,0x90,0x68,0x00, // 0x26 & + 0x60,0x20,0x40,0x00,0x00,0x00,0x00,0x00, // 0x27 ' + 0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00, // 0x28 ( + 0x40,0x20,0x10,0x10,0x10,0x20,0x40,0x00, // 0x29 ) + 0x00,0xa8,0x70,0x20,0x70,0xa8,0x00,0x00, // 0x2a * + 0x00,0x20,0x20,0xf8,0x20,0x20,0x00,0x00, // 0x2b + + 0x00,0x00,0x00,0x00,0x00,0x60,0x20,0x40, // 0x2c , + 0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x00, // 0x2d - + 0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00, // 0x2e . + 0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00, // 0x2f / + 0x70,0x88,0x98,0xa8,0xc8,0x88,0x70,0x00, // 0x30 0 + 0x20,0x60,0x20,0x20,0x20,0x20,0xf8,0x00, // 0x31 1 + 0x70,0x88,0x08,0x10,0x60,0x80,0xf8,0x00, // 0x32 2 + 0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00, // 0x33 3 + 0x30,0x50,0x90,0x90,0xf8,0x10,0x10,0x00, // 0x34 4 + 0xf8,0x80,0xf0,0x08,0x08,0x08,0xf0,0x00, // 0x35 5 + 0x30,0x40,0x80,0xf0,0x88,0x88,0x70,0x00, // 0x36 6 + 0xf8,0x08,0x10,0x20,0x20,0x20,0x20,0x00, // 0x37 7 + 0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00, // 0x38 8 + 0x70,0x88,0x88,0x78,0x08,0x10,0x60,0x00, // 0x39 9 + 0x00,0x00,0x00,0x60,0x00,0x60,0x00,0x00, // 0x3a : + 0x00,0x00,0x00,0x60,0x00,0x60,0x20,0x40, // 0x3b ; + 0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00, // 0x3c < + 0x00,0x00,0xf8,0x00,0xf8,0x00,0x00,0x00, // 0x3d = + 0x08,0x04,0x02,0x01,0x02,0x04,0x08,0x00, // 0x3e > + 0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00, // 0x3f ? + 0x70,0x88,0x98,0xa8,0x98,0x80,0x70,0x00, // 0x40 @ + 0x20,0x50,0x88,0x88,0xf8,0x88,0x88,0x00, // 0x41 A + 0xf0,0x88,0x88,0xf0,0x88,0x88,0xf0,0x00, // 0x42 B + 0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00, // 0x43 C + 0xf0,0x88,0x88,0x88,0x88,0x88,0xf0,0x00, // 0x44 D + 0xf8,0x80,0x80,0xf0,0x80,0x80,0xf8,0x00, // 0x45 E + 0xf8,0x80,0x80,0xf0,0x80,0x80,0x80,0x00, // 0x46 F + 0x70,0x88,0x80,0xb8,0x88,0x88,0x70,0x00, // 0x47 G + 0x88,0x88,0x88,0xf8,0x88,0x88,0x88,0x00, // 0x48 H + 0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00, // 0x49 I + 0x08,0x08,0x08,0x08,0x88,0x88,0x70,0x00, // 0x4A J + 0x88,0x90,0xa0,0xc0,0xa0,0x90,0x88,0x00, // 0x4B K + 0x80,0x80,0x80,0x80,0x80,0x80,0xf8,0x00, // 0x4C L + 0x88,0xd8,0xa8,0xa8,0x88,0x88,0x88,0x00, // 0x4D M + 0x88,0xc8,0xc8,0xa8,0x98,0x98,0x88,0x00, // 0x4E N + 0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00, // 0x4F O + 0xf0,0x88,0x88,0xf0,0x80,0x80,0x80,0x00, // 0x50 P + 0x70,0x88,0x88,0x88,0x88,0xa8,0x90,0x68, // 0x51 Q + 0xf0,0x88,0x88,0xf0,0xa0,0x90,0x88,0x00, // 0x52 R + 0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00, // 0x53 S + 0xf8,0x20,0x20,0x20,0x20,0x20,0x20,0x00, // 0x54 T + 0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00, // 0x55 U + 0x88,0x88,0x88,0x88,0x50,0x50,0x20,0x00, // 0x56 V + 0x88,0x88,0x88,0xa8,0xa8,0xd8,0x88,0x00, // 0x57 W + 0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00, // 0x58 X + 0x88,0x88,0x88,0x70,0x20,0x20,0x20,0x00, // 0x59 Y + 0xf8,0x08,0x10,0x20,0x40,0x80,0xf8,0x00, // 0x5A Z + 0x78,0x60,0x60,0x60,0x60,0x60,0x78,0x00, // 0x5B [ + 0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00, // 0x5C backslash + 0xF0,0x30,0x30,0x30,0x30,0x30,0xF0,0x00, // 0x5D ] + 0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00, // 0x5E + 0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x00, // 0x5F _ + 0x40,0x20,0x10,0x00,0x00,0x00,0x00,0x00, // 0x60 + 0x00,0x00,0x68,0x98,0x88,0x98,0x68,0x00, // 0x61 a + 0x80,0x80,0xf0,0x88,0x88,0x88,0xf0,0x00, // 0x62 b + 0x00,0x00,0x78,0x80,0x80,0x80,0x78,0x00, // 0x63 c + 0x08,0x08,0x68,0x98,0x88,0x98,0x68,0x00, // 0x64 d + 0x00,0x00,0x70,0x88,0xf8,0x80,0x70,0x00, // 0x65 e + 0x30,0x48,0x40,0xe0,0x40,0x40,0x40,0x00, // 0x66 f + 0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x70, // 0x67 g + 0x80,0x80,0xf0,0x88,0x88,0x88,0x88,0x00, // 0x68 h + 0x20,0x00,0x60,0x20,0x20,0x20,0x70,0x00, // 0x69 i + 0x08,0x00,0x18,0x08,0x88,0x88,0x70,0x00, // 0x6a j + 0x80,0x80,0x88,0x90,0xe0,0x90,0x88,0x00, // 0x6b k + 0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00, // 0x6c l + 0x00,0x00,0xd0,0xa8,0xa8,0xa8,0xa8,0x00, // 0x6d m + 0x00,0x00,0xb0,0xc8,0x88,0x88,0x88,0x00, // 0x6e n + 0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00, // 0x6f o + 0x00,0x00,0xf0,0x88,0x88,0x88,0xf0,0x80, // 0x70 p + 0x00,0x00,0x78,0x88,0x88,0x88,0x78,0x08, // 0x71 q + 0x00,0x00,0xb8,0xc0,0x80,0x80,0x80,0x00, // 0x72 r + 0x00,0x00,0x78,0x80,0x70,0x08,0xf0,0x00, // 0x73 s + 0x20,0x20,0xf8,0x20,0x20,0x20,0x20,0x00, // 0x74 t + 0x00,0x00,0x88,0x88,0x88,0x98,0x68,0x00, // 0x75 u + 0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00, // 0x76 v + 0x00,0x00,0x88,0xa8,0xa8,0xa8,0x50,0x00, // 0x77 w + 0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00, // 0x78 x + 0x00,0x00,0x88,0x88,0x98,0x68,0x08,0x70, // 0x79 y + 0x00,0x00,0xf8,0x10,0x20,0x40,0xf8,0x00, // 0x7a z + 0x18,0x20,0x20,0x40,0x20,0x20,0x18,0x00, // 0x7b { + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x00, // 0x7c | + 0xc0,0x20,0x20,0x10,0x20,0x20,0xc0,0x00, // 0x7d } + 0x00,0x00,0x40,0xa8,0x10,0x00,0x00,0x00, // 0x7e + 0x70,0x70,0x20,0xf8,0x20,0x70,0x50,0x00, // 0x7f + }; + int c; + + memset(page, 0, CHRROM_PAGE); + for (c = 0; c < 0x60; c++) { + memcpy(page + (c + 32) * 16, &font[c * 8], 8); + memcpy(page + (c + 32) * 16 + 8, &font[c * 8], 8); + } +} + /* ** Prototypes */ @@ -278,6 +405,7 @@ void accumulated_push(enum lexical_component, int, char *); void compile_assignment(int); void compile_statement(int); void compile_basic(void); +int process_variables(void); /* ** Emit an error @@ -285,7 +413,7 @@ void compile_basic(void); void emit_error(char *string) { fprintf(stderr, "ERROR: %s at line %d (%s)\n", string, current_line, current_file); - err_code = 1; + err_code = EXIT_FAILURE; } /* @@ -298,29 +426,52 @@ void emit_warning(char *string) fprintf(stderr, "Warning: %s at line %d (%s)\n", string, current_line, current_file); } +/* + ** Emit info + */ +void emit_info(char *string) +{ + fprintf(stderr, "INFO: %s at line %d (%s)\n", string, current_line, current_file); +} /* ** Finish a bank */ void bank_finish(void) { - if (machine == SG1000) { + if (machine == SG1000 || machine == SMS) { if (bank_current == 0) { - fprintf(output, "BANK_0_FREE:\tEQU $3fff-$\n"); - fprintf(output, "\tTIMES $3fff-$ DB $ff\n"); + fprintf(output, "BANK_0_FREE:\tEQU $3fbf-$\n"); + fprintf(output, "\tTIMES $3fbf-$ DB $ff\n"); + } else if (bank_current == 7 && option_fm != 0) { + fprintf(output, "BANK_%d_FREE:\tEQU $7d00-$\n", bank_current); + fprintf(output, "\tTIMES $7fbf-$ DB $ff\n"); } else { - fprintf(output, "BANK_%d_FREE:\tEQU $7fff-$\n", bank_current); - fprintf(output, "\tTIMES $7fff-$ DB $ff\n"); + fprintf(output, "BANK_%d_FREE:\tEQU $7fbf-$\n", bank_current); + fprintf(output, "\tTIMES $7fbf-$ DB $ff\n"); } fprintf(output, "\tDB $%02x\n", bank_current); - } else if (machine == MSX) { + fprintf(output, "\tTIMES $40 DB $ff\n"); + } else if (machine == MSX || machine == MSX2) { if (bank_current == 0) { fprintf(output, "BANK_0_FREE:\tEQU $7fff-$\n"); fprintf(output, "\tTIMES $7fff-$ DB $ff\n"); + } else if (bank_current == 7 && option_fm != 0) { + fprintf(output, "BANK_%d_FREE:\tEQU $bd00-$\n", bank_current); + fprintf(output, "\tTIMES $bfff-$ DB $ff\n"); } else { fprintf(output, "BANK_%d_FREE:\tEQU $bfff-$\n", bank_current); fprintf(output, "\tTIMES $bfff-$ DB $ff\n"); } - fprintf(output, "\tDB $%02x\n", bank_current); + fprintf(output, "\tDB $%02x\n", bank_konami ? bank_current * 2 : bank_current); + } else if (machine == NES) { + if (bank_current == 0) { + fprintf(output, "BANK_0_FREE:\tEQU $fffa-$\n"); + fprintf(output, "\tTIMES $fffa-$ DB $ff\n"); + } else { + fprintf(output, "BANK_%d_FREE:\tEQU $bfff-$\n", bank_current); + fprintf(output, "\tTIMES $bfff-$ DB $ff\n"); + } + fprintf(output, "\tDB $%02x\n", (bank_current - 1) & 0x1f); } else if (machine == TI994A) { if (bank_current == 0) { // bank 0 is copied to RAM so is 24k @@ -403,7 +554,7 @@ struct label *function_add(char *name) new_one = malloc(sizeof(struct label) + strlen(name)); if (new_one == NULL) { fprintf(stderr, "Out of memory\n"); - exit(1); + exit(EXIT_FAILURE); } new_one->used = 0; new_one->length = 0; @@ -441,7 +592,7 @@ struct signedness *signed_add(char *name) new_one = malloc(sizeof(struct signedness) + strlen(name)); if (new_one == NULL) { fprintf(stderr, "Out of memory\n"); - exit(1); + exit(EXIT_FAILURE); } new_one->sign = 0; strcpy(new_one->name, name); @@ -478,11 +629,11 @@ struct constant *constant_add(char *name) new_one = malloc(sizeof(struct constant) + strlen(name)); if (new_one == NULL) { fprintf(stderr, "Out of memory\n"); - exit(1); + exit(EXIT_FAILURE); } new_one->value = 0; strcpy(new_one->name, name); - previous = &constant_hash[label_hash_value(name)]; + previous = &constant_hash[label_hash_value(new_one->name)]; new_one->next = *previous; *previous = new_one; return new_one; @@ -515,7 +666,7 @@ struct label *label_add(char *name) new_one = malloc(sizeof(struct label) + strlen(name)); if (new_one == NULL) { fprintf(stderr, "Out of memory\n"); - exit(1); + exit(EXIT_FAILURE); } new_one->used = 0; strcpy(new_one->name, name); @@ -553,7 +704,7 @@ struct label *array_add(char *name) new_one = malloc(sizeof(struct label) + strlen(name)); if (new_one == NULL) { fprintf(stderr, "Out of memory\n"); - exit(1); + exit(EXIT_FAILURE); } new_one->used = 0; strcpy(new_one->name, name); @@ -590,7 +741,7 @@ struct macro *macro_add(char *name) new_one = malloc(sizeof(struct macro) + strlen(name)); if (new_one == NULL) { fprintf(stderr, "Out of memory\n"); - exit(1); + exit(EXIT_FAILURE); } new_one->total_arguments = 0; new_one->in_use = 0; @@ -648,8 +799,14 @@ void get_lex(void) { if (accumulated.length > 0) { --accumulated.length; lex = accumulated.definition[accumulated.length].lex; - strcpy(name, accumulated.definition[accumulated.length].name); value = accumulated.definition[accumulated.length].value; + if (lex == C_STRING) { + name_size = value; + if (name_size) + memcpy(name, accumulated.definition[accumulated.length].name, name_size); + } else { + strcpy(name, accumulated.definition[accumulated.length].name); + } free(accumulated.definition[accumulated.length].name); return; } @@ -680,6 +837,10 @@ void get_lex(void) { if (p - name < MAX_LINE_SIZE - 1) *p++ = toupper(line[line_pos]); line_pos++; + if (p - name == 3 && name[0] == 'C' && name[1] == 'H' && name[2] == 'R' && line[line_pos] == '$' && line[line_pos + 1] != ':') { /* Hack to support CHR$ */ + *p++ = line[line_pos]; + line_pos++; + } } *p = '\0'; name_size = (int) (p - name); @@ -969,6 +1130,14 @@ int evaluate_expression(int cast, int to_type, int label) tree->right->type = N_NUM8; tree->type = N_AND8; type = TYPE_8; + } else if (tree->type == N_EXTEND8 && tree->left->type == N_AND8) { + struct node *temp; + + temp = tree; + tree = temp->left; + temp->left = NULL; + node_delete(temp); + type = TYPE_8; } } if (label != 0 && (tree->type == N_NUM8 || tree->type == N_NUM16)) { @@ -1000,6 +1169,14 @@ int evaluate_expression(int cast, int to_type, int label) return type; } +/* + ** Check for macro replacement + */ +void check_for_macro(void) { + if (lex == C_NAME && macro_search(name) != NULL) /* Function (macro) */ + replace_macro(); +} + /* ** Expression evaluation level 0 (OR) */ @@ -1009,10 +1186,12 @@ struct node *evaluate_level_0(int *type) struct node *right; int type2; + check_for_macro(); left = evaluate_level_1(type); while (1) { if (lex == C_NAME && strcmp(name, "OR") == 0) { get_lex(); + check_for_macro(); right = evaluate_level_1(&type2); *type = mix_types(&left, *type, &right, type2); if ((*type & MAIN_TYPE) == TYPE_8) @@ -1035,10 +1214,12 @@ struct node *evaluate_level_1(int *type) struct node *right; int type2; + check_for_macro(); left = evaluate_level_2(type); while (1) { if (lex == C_NAME && strcmp(name, "XOR") == 0) { get_lex(); + check_for_macro(); right = evaluate_level_2(&type2); *type = mix_types(&left, *type, &right, type2); if ((*type & MAIN_TYPE) == TYPE_8) @@ -1061,10 +1242,12 @@ struct node *evaluate_level_2(int *type) struct node *right; int type2; + check_for_macro(); left = evaluate_level_3(type); while (1) { if (lex == C_NAME && strcmp(name, "AND") == 0) { get_lex(); + check_for_macro(); right = evaluate_level_3(&type2); *type = mix_types(&left, *type, &right, type2); if ((*type & MAIN_TYPE) == TYPE_8) @@ -1091,6 +1274,7 @@ struct node *evaluate_level_3(int *type) enum node_type operation8s; enum node_type operation16s; + check_for_macro(); left = evaluate_level_4(type); while (1) { if (lex == C_EQUAL) { @@ -1127,6 +1311,7 @@ struct node *evaluate_level_3(int *type) break; } get_lex(); + check_for_macro(); right = evaluate_level_4(&type2); *type = mix_types(&left, *type, &right, type2); if ((*type & MAIN_TYPE) == TYPE_8) @@ -1146,28 +1331,29 @@ struct node *evaluate_level_4(int *type) struct node *left; struct node *right; int type2; - + enum node_type operation8; + enum node_type operation16; + + check_for_macro(); left = evaluate_level_5(type); while (1) { if (lex == C_PLUS) { - get_lex(); - right = evaluate_level_5(&type2); - *type = mix_types(&left, *type, &right, type2); - if ((*type & MAIN_TYPE) == TYPE_8) - left = node_create(N_PLUS8, 0, left, right); - else - left = node_create(N_PLUS16, 0, left, right); + operation8 = N_PLUS8; + operation16 = N_PLUS16; } else if (lex == C_MINUS) { - get_lex(); - right = evaluate_level_5(&type2); - *type = mix_types(&left, *type, &right, type2); - if ((*type & MAIN_TYPE) == TYPE_8) - left = node_create(N_MINUS8, 0, left, right); - else - left = node_create(N_MINUS16, 0, left, right); + operation8 = N_MINUS8; + operation16 = N_MINUS16; } else { break; } + get_lex(); + check_for_macro(); + right = evaluate_level_5(&type2); + *type = mix_types(&left, *type, &right, type2); + if ((*type & MAIN_TYPE) == TYPE_8) + left = node_create(operation8, 0, left, right); + else + left = node_create(operation16, 0, left, right); } return left; } @@ -1180,33 +1366,32 @@ struct node *evaluate_level_5(int *type) struct node *left; struct node *right; int type2; - + enum node_type operation16; + enum node_type operation16s; + + check_for_macro(); left = evaluate_level_6(type); while (1) { if (lex == C_MUL) { - get_lex(); - right = evaluate_level_6(&type2); - *type = extend_types(&left, *type, &right, type2); - left = node_create(N_MUL16, 0, left, right); + operation16 = N_MUL16; + operation16s = N_MUL16; } else if (lex == C_DIV) { - get_lex(); - right = evaluate_level_6(&type2); - *type = extend_types(&left, *type, &right, type2); - if (*type & TYPE_SIGNED) - left = node_create(N_DIV16S, 0, left, right); - else - left = node_create(N_DIV16, 0, left, right); + operation16 = N_DIV16; + operation16s = N_DIV16S; } else if (lex == C_MOD) { - get_lex(); - right = evaluate_level_6(&type2); - *type = extend_types(&left, *type, &right, type2); - if (*type & TYPE_SIGNED) - left = node_create(N_MOD16S, 0, left, right); - else - left = node_create(N_MOD16, 0, left, right); + operation16 = N_MOD16; + operation16s = N_MOD16S; } else { break; } + get_lex(); + check_for_macro(); + right = evaluate_level_6(&type2); + *type = extend_types(&left, *type, &right, type2); + if (*type & TYPE_SIGNED) + left = node_create(operation16s, 0, left, right); + else + left = node_create(operation16, 0, left, right); } return left; } @@ -1220,21 +1405,22 @@ struct node *evaluate_level_6(int *type) if (lex == C_MINUS) { get_lex(); - - left = evaluate_level_7(type); + check_for_macro(); + left = evaluate_level_6(type); if ((*type & MAIN_TYPE) == TYPE_8) left = node_create(N_NEG8, 0, left, NULL); else left = node_create(N_NEG16, 0, left, NULL); } else if (lex == C_NAME && strcmp(name, "NOT") == 0) { get_lex(); - - left = evaluate_level_7(type); + check_for_macro(); + left = evaluate_level_6(type); if ((*type & MAIN_TYPE) == TYPE_8) left = node_create(N_NOT8, 0, left, NULL); else left = node_create(N_NOT16, 0, left, NULL); } else { + check_for_macro(); left = evaluate_level_7(type); } return left; @@ -1288,8 +1474,8 @@ struct node *evaluate_level_7(int *type) } get_lex(); *type = TYPE_8; - if ((machine == CREATIVISION)||(machine == TI994A)) - emit_warning("Ignoring INP (not supported in Creativision or TI994A)"); + if (machine == CREATIVISION || machine == TI994A || machine == NES) + emit_warning("Ignoring INP (not supported in this platform)"); return tree; } if (strcmp(name, "PEEK") == 0) { @@ -1310,6 +1496,8 @@ struct node *evaluate_level_7(int *type) return tree; } if (strcmp(name, "VPEEK") == 0) { + if (machine == NES) + emit_error("VPEEK cannot be used in NES/Famicom"); get_lex(); if (lex != C_LPAREN) { emit_error("missing left parenthesis in VPEEK"); @@ -1358,144 +1546,89 @@ struct node *evaluate_level_7(int *type) *type = TYPE_16 | TYPE_SIGNED; return tree; } - if (strcmp(name, "CONT") == 0) { + if (strcmp(name, "CONT") == 0 || strcmp(name, "CONT1") == 0 || strcmp(name, "CONT2") == 0) { + struct node *tree = NULL; + int cont; + + *type = TYPE_8; + if (strcmp(name, "CONT1") == 0) + cont = 1; + else if (strcmp(name, "CONT2") == 0) + cont = 2; + else + cont = 0; get_lex(); if (lex == C_PERIOD) { get_lex(); - if (lex != C_NAME) { - tree = node_create(N_JOY1, 0, NULL, NULL); - emit_error("CONT syntax error"); - } else if (strcmp(name, "UP") == 0) { - get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); - tree = node_create(N_OR8, 0, tree, node_create(N_JOY2, 0, NULL, NULL)); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 1, NULL, NULL)); - } else if (strcmp(name, "RIGHT") == 0) { - get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); - tree = node_create(N_OR8, 0, tree, node_create(N_JOY2, 0, NULL, NULL)); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 2, NULL, NULL)); - } else if (strcmp(name, "DOWN") == 0) { - get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); - tree = node_create(N_OR8, 0, tree, node_create(N_JOY2, 0, NULL, NULL)); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 4, NULL, NULL)); - } else if (strcmp(name, "LEFT") == 0) { - get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); - tree = node_create(N_OR8, 0, tree, node_create(N_JOY2, 0, NULL, NULL)); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 8, NULL, NULL)); - } else if (strcmp(name, "BUTTON") == 0) { - get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); - tree = node_create(N_OR8, 0, tree, node_create(N_JOY2, 0, NULL, NULL)); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 0x40, NULL, NULL)); - } else if (strcmp(name, "BUTTON2") == 0) { - get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); - tree = node_create(N_OR8, 0, tree, node_create(N_JOY2, 0, NULL, NULL)); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 0x80, NULL, NULL)); + if (strcmp(name, "SPINNER") == 0) { + if (machine != COLECOVISION && machine != COLECOVISION_SGM) { + emit_error("SPINNER is only available for Colecovision"); + tree = node_create(N_NUM8, 0, NULL, NULL); + } else if (cont == 1) { + tree = node_create(N_SPINNER1, 0, NULL, NULL); + } else if (cont == 2) { + tree = node_create(N_SPINNER2, 0, NULL, NULL); + } else { + emit_error("Using CONT.SPINNER instead of CONT1 or CONT2"); + tree = node_create(N_NUM8, 0, NULL, NULL); + } + tree = node_create(N_EXTEND8S, 0, tree, NULL); + *type = TYPE_16; + spinner_used = 1; } else if (strcmp(name, "KEY") == 0) { - get_lex(); - tree = node_create(N_KEY1, 0, NULL, NULL); - tree = node_create(N_AND8, 0, tree, node_create(N_KEY2, 0, NULL, NULL)); + if (cont == 1) { + tree = node_create(N_KEY1, 0, NULL, NULL); + } else if (cont == 2) { + tree = node_create(N_KEY2, 0, NULL, NULL); + } else { + tree = node_create(N_KEY1, 0, NULL, NULL); + tree = node_create(N_AND8, 0, tree, node_create(N_KEY2, 0, NULL, NULL)); + } } else { - tree = node_create(N_JOY1, 0, NULL, NULL); - emit_error("Wrong field for CONT"); + if (cont == 1) { + tree = node_create(N_JOY1, 0, NULL, NULL); + } else if (cont == 2) { + tree = node_create(N_JOY2, 0, NULL, NULL); + } else { + tree = node_create(N_JOY1, 0, NULL, NULL); + tree = node_create(N_OR8, 0, tree, node_create(N_JOY2, 0, NULL, NULL)); + } } - } else { - tree = node_create(N_JOY1, 0, NULL, NULL); - tree = node_create(N_OR8, 0, tree, node_create(N_JOY2, 0, NULL, NULL)); - } - *type = TYPE_8; - return tree; - } - if (strcmp(name, "CONT1") == 0) { - get_lex(); - if (lex == C_PERIOD) { - get_lex(); if (lex != C_NAME) { - tree = node_create(N_JOY1, 0, NULL, NULL); - emit_error("CONT1 syntax error"); + emit_error("CONT syntax error"); } else if (strcmp(name, "UP") == 0) { get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 1, NULL, NULL)); } else if (strcmp(name, "RIGHT") == 0) { get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 2, NULL, NULL)); } else if (strcmp(name, "DOWN") == 0) { get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 4, NULL, NULL)); } else if (strcmp(name, "LEFT") == 0) { get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 8, NULL, NULL)); } else if (strcmp(name, "BUTTON") == 0) { get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 0x40, NULL, NULL)); } else if (strcmp(name, "BUTTON2") == 0) { get_lex(); - tree = node_create(N_JOY1, 0, NULL, NULL); tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 0x80, NULL, NULL)); - } else if (strcmp(name, "KEY") == 0) { + } else if (strcmp(name, "KEY") == 0 || strcmp(name, "SPINNER") == 0) { get_lex(); - tree = node_create(N_KEY1, 0, NULL, NULL); } else { - emit_error("Wrong field for CONT1"); - tree = node_create(N_JOY1, 0, NULL, NULL); + emit_error("Wrong field for CONT"); } } else { - tree = node_create(N_JOY1, 0, NULL, NULL); - } - *type = TYPE_8; - return tree; - } - if (strcmp(name, "CONT2") == 0) { - get_lex(); - if (lex == C_PERIOD) { - get_lex(); - if (lex != C_NAME) { - emit_error("CONT2 syntax error"); - tree = node_create(N_JOY2, 0, NULL, NULL); - } else if (strcmp(name, "UP") == 0) { - get_lex(); - tree = node_create(N_JOY2, 0, NULL, NULL); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 1, NULL, NULL)); - } else if (strcmp(name, "RIGHT") == 0) { - get_lex(); - tree = node_create(N_JOY2, 0, NULL, NULL); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 2, NULL, NULL)); - } else if (strcmp(name, "DOWN") == 0) { - get_lex(); - tree = node_create(N_JOY2, 0, NULL, NULL); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 4, NULL, NULL)); - } else if (strcmp(name, "LEFT") == 0) { - get_lex(); - tree = node_create(N_JOY2, 0, NULL, NULL); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 8, NULL, NULL)); - } else if (strcmp(name, "BUTTON") == 0) { - get_lex(); - tree = node_create(N_JOY2, 0, NULL, NULL); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 0x40, NULL, NULL)); - } else if (strcmp(name, "BUTTON2") == 0) { - get_lex(); + if (cont == 1) { + tree = node_create(N_JOY1, 0, NULL, NULL); + } else if (cont == 2) { tree = node_create(N_JOY2, 0, NULL, NULL); - tree = node_create(N_AND8, 0, tree, node_create(N_NUM8, 0x80, NULL, NULL)); - } else if (strcmp(name, "KEY") == 0) { - get_lex(); - tree = node_create(N_KEY2, 0, NULL, NULL); } else { - emit_error("Wrong field for CONT2"); - tree = node_create(N_JOY2, 0, NULL, NULL); + tree = node_create(N_JOY1, 0, NULL, NULL); + tree = node_create(N_OR8, 0, tree, node_create(N_JOY2, 0, NULL, NULL)); } - } else { - tree = node_create(N_JOY2, 0, NULL, NULL); } - *type = TYPE_8; return tree; } if (strcmp(name, "RANDOM") == 0) { @@ -1523,18 +1656,31 @@ struct node *evaluate_level_7(int *type) return tree; } if (strcmp(name, "MUSIC") == 0) { + int c; + get_lex(); if (lex != C_PERIOD) { emit_error("missing period in MUSIC"); } else { get_lex(); } - if (lex != C_NAME || strcmp(name, "PLAYING") != 0) { - emit_error("only allowed MUSIC.PLAYING"); + if (lex != C_NAME) { + emit_error("syntax error in MUSIC"); + c = 0; + } else if (strcmp(name, "PLAYING") == 0) { + get_lex(); + c = 0; + } else if (strcmp(name, "FM_AVAILABLE") == 0 && (machine == MSX || machine == MSX2 || machine == SMS)) { + get_lex(); + c = 1; + } else if (strcmp(name, "FM_ENABLED") == 0 && (machine == MSX || machine == MSX2 || machine == SMS)) { + get_lex(); + c = 2; } else { + emit_error("unhandled variable in MUSIC"); get_lex(); } - tree = node_create(N_MUSIC, 0, NULL, NULL); + tree = node_create(N_MUSIC, c, NULL, NULL); *type = TYPE_8; return tree; } @@ -1569,6 +1715,7 @@ struct node *evaluate_level_7(int *type) if (strcmp(name, "VARPTR") == 0) { /* Access to variable/array/label address */ *type = TYPE_16; get_lex(); + check_for_macro(); if (lex != C_NAME) { emit_error("missing variable name for VARPTR"); return node_create(N_NUM16, 0, NULL, NULL); @@ -1656,6 +1803,7 @@ struct node *evaluate_level_7(int *type) emit_error("missing right parenthesis in LEN"); else get_lex(); + *type = TYPE_16; return node_create(N_NUM16, c, NULL, NULL); } if (strcmp(name, "POS") == 0) { /* Access to current screen position */ @@ -1673,12 +1821,11 @@ struct node *evaluate_level_7(int *type) else get_lex(); *type = TYPE_16; - return node_create(N_MINUS16, 0, node_create(N_POS, 0, NULL, NULL), node_create(N_NUM16, 0x1800, NULL, NULL)); - } - if (macro_search(name) != NULL) { /* Function (macro) */ - if (replace_macro()) - return node_create(N_NUM8, 0, NULL, NULL); - return evaluate_level_0(type); + tree = node_create(N_POS, 0, NULL, NULL); + tree = node_create(N_MINUS16, 0, tree, node_create(N_NUM16, (machine == SMS) ? 0x3800 : 0x1800, NULL, NULL)); + if (machine == SMS) + tree = node_create(N_DIV16, 0, tree, node_create(N_NUM16, 2, NULL, NULL)); + return tree; } if (lex_sneak_peek() == '(') { /* Indexed access */ int type2; @@ -1723,7 +1870,7 @@ struct node *evaluate_level_7(int *type) tree = node_create(N_MUL16, 0, tree, node_create(N_NUM16, 2, NULL, NULL)); } - tree = node_create(*type == TYPE_16 ? N_PEEK16 : N_PEEK8, 0, + tree = node_create((*type & MAIN_TYPE) == TYPE_16 ? N_PEEK16 : N_PEEK8, 0, node_create(N_PLUS16, 0, addr, tree), NULL); return tree; } @@ -1782,7 +1929,7 @@ struct node *evaluate_level_7(int *type) *type = TYPE_16; return node_create(N_NUM16, temp & 0xffff, NULL, NULL); } - emit_error("bad syntax por expression"); + emit_error("bad syntax for expression"); *type = TYPE_16; return node_create(N_NUM16, 0, NULL, NULL); } @@ -1854,18 +2001,26 @@ void accumulated_push(enum lexical_component lex, int value, char *name) accumulated.definition = realloc(accumulated.definition, (accumulated.max_length + 1) * 2 * sizeof(struct macro_def)); if (accumulated.definition == NULL) { emit_error("out of memory in accumulated_push"); - exit(1); + exit(EXIT_FAILURE); } accumulated.max_length = (accumulated.max_length + 1) * 2; } accumulated.definition[accumulated.length].lex = lex; accumulated.definition[accumulated.length].value = value; - accumulated.definition[accumulated.length].name = malloc(strlen(name) + 1); + if (lex == C_STRING) + accumulated.definition[accumulated.length].name = malloc(value + 1); + else + accumulated.definition[accumulated.length].name = malloc(strlen(name) + 1); if (accumulated.definition[accumulated.length].name == NULL) { emit_error("out of memory in accumulated_push"); - exit(1); + exit(EXIT_FAILURE); + } + if (lex == C_STRING) { + if (value > 0) + memcpy(accumulated.definition[accumulated.length].name, name, value); + } else { + strcpy(accumulated.definition[accumulated.length].name, name); } - strcpy(accumulated.definition[accumulated.length].name, name); accumulated.length++; } @@ -1923,13 +2078,23 @@ int replace_macro(void) argument[c].max_length = (argument[c].max_length + 1) * 2; } argument[c].definition[argument[c].length].lex = lex; + if (lex == C_STRING) + value = name_size; argument[c].definition[argument[c].length].value = value; - argument[c].definition[argument[c].length].name = malloc(strlen(name) + 1); + if (lex == C_STRING) + argument[c].definition[argument[c].length].name = malloc(name_size + 1); + else + argument[c].definition[argument[c].length].name = malloc(strlen(name) + 1); if (argument[c].definition[argument[c].length].name == NULL) { emit_error("out of memory in call to FN"); return 1; } - strcpy(argument[c].definition[argument[c].length].name, name); + if (lex == C_STRING) { + if (name_size) + memcpy(argument[c].definition[argument[c].length].name, name, name_size); + } else { + strcpy(argument[c].definition[argument[c].length].name, name); + } argument[c].length++; get_lex(); } @@ -2114,6 +2279,10 @@ void compile_statement(int check_for_else) while (1) { if (lex == C_NAME) { last_is_return = 0; + + /* + ** CVBasic core language + */ if (strcmp(name, "GOTO") == 0) { get_lex(); if (lex != C_NAME) { @@ -2174,7 +2343,7 @@ void compile_statement(int check_for_else) new_loop = malloc(sizeof(struct loop)); if (new_loop == NULL) { fprintf(stderr, "out of memory\n"); - exit(1); + exit(EXIT_FAILURE); } new_loop->type = NESTED_IF; new_loop->step = NULL; @@ -2313,7 +2482,6 @@ void compile_statement(int check_for_else) struct node *var; int positive; int type; - int step_value; int type_var; enum node_type comparison; struct signedness *sign; @@ -2329,7 +2497,7 @@ void compile_statement(int check_for_else) new_loop = malloc(sizeof(struct loop) + strlen(assigned) + 1); if (new_loop == NULL) { fprintf(stderr, "Out of memory\n"); - exit(1); + exit(EXIT_FAILURE); } strcpy(new_loop->var, assigned); if (assigned[0] == '#') @@ -2351,32 +2519,30 @@ void compile_statement(int check_for_else) final = node_create((type & TYPE_SIGNED) ? N_EXTEND8S : N_EXTEND8, 0, final, NULL); else if ((type_var & MAIN_TYPE) == TYPE_8 && (type & MAIN_TYPE) == TYPE_16) final = node_create(N_REDUCE16, 0, final, NULL); - positive = 1; + positive = 2; var = node_create((type_var & MAIN_TYPE) == TYPE_16 ? N_LOAD16 : N_LOAD8, 0, NULL, NULL); var->label = label_search(new_loop->var); if (lex == C_NAME && strcmp(name, "STEP") == 0) { get_lex(); if (lex == C_MINUS) { get_lex(); - step = evaluate_level_0(&type); - if ((type_var & MAIN_TYPE) == TYPE_16 && (type & MAIN_TYPE) == TYPE_8) - step = node_create((type & TYPE_SIGNED) ? N_EXTEND8S : N_EXTEND8, 0, step, NULL); - else if ((type_var & MAIN_TYPE) == TYPE_8 && (type & MAIN_TYPE) == TYPE_16) - step = node_create(N_REDUCE16, 0, step, NULL); - step = node_create((type_var & MAIN_TYPE) == TYPE_16 ? N_MINUS16 : N_MINUS8, 0, - var, step); positive = 0; - } else { - step = evaluate_level_0(&type); - if ((type_var & MAIN_TYPE) == TYPE_16 && (type & MAIN_TYPE) == TYPE_8) - step = node_create((type & TYPE_SIGNED) ? N_EXTEND8S : N_EXTEND8, 0, step, NULL); - else if ((type_var & MAIN_TYPE) == TYPE_8 && (type & MAIN_TYPE) == TYPE_16) - step = node_create(N_REDUCE16, 0, step, NULL); - step = node_create((type_var & MAIN_TYPE) == TYPE_16 ? N_PLUS16 : N_PLUS8, 0, var, step); } + step = evaluate_level_0(&type); + if ((type_var & MAIN_TYPE) == TYPE_16 && (type & MAIN_TYPE) == TYPE_8) + step = node_create((type & TYPE_SIGNED) ? N_EXTEND8S : N_EXTEND8, 0, step, NULL); + else if ((type_var & MAIN_TYPE) == TYPE_8 && (type & MAIN_TYPE) == TYPE_16) + step = node_create(N_REDUCE16, 0, step, NULL); + if ((step->type == N_NUM8 && step->value == 1) + || (step->type == N_NUM16 && step->value == 1)) + positive |= 1; } else { - step_value = 1; step = node_create((type_var & MAIN_TYPE) == TYPE_16 ? N_NUM16 : N_NUM8, 1, NULL, NULL); + positive |= 1; + } + if ((positive & 2) == 0) { /* Negative step */ + step = node_create((type_var & MAIN_TYPE) == TYPE_16 ? N_MINUS16 : N_MINUS8, 0, var, step); + } else { /* Positive step */ step = node_create((type_var & MAIN_TYPE) == TYPE_16 ? N_PLUS16 : N_PLUS8, 0, var, step); } var = node_create(N_ADDR, 0, NULL, NULL); @@ -2385,15 +2551,37 @@ void compile_statement(int check_for_else) var = node_create((type_var & MAIN_TYPE) == TYPE_16 ? N_LOAD16 : N_LOAD8, 0, NULL, NULL); var->label = label_search(new_loop->var); if ((type_var & MAIN_TYPE) == TYPE_16) { - if (type_var & TYPE_SIGNED) - comparison = positive ? N_GREATER16S : N_LESS16S; - else - comparison = positive ? N_GREATER16 : N_LESS16; + if (type_var & TYPE_SIGNED) { + if (final->type == N_NUM16 && final->value == 0x8000 && (positive & 1) != 0) { + final->value = 0x7fff; + comparison = N_EQUAL16; + } else { + comparison = (positive & 2) ? N_GREATER16S : N_LESS16S; + } + } else { + if (final->type == N_NUM16 && final->value == 0x0000 && (positive & 1) != 0) { + final->value = 0xffff; + comparison = N_EQUAL16; + } else { + comparison = (positive & 2) ? N_GREATER16 : N_LESS16; + } + } } else { - if (type_var & TYPE_SIGNED) - comparison = positive ? N_GREATER8S : N_LESS8S; - else - comparison = positive ? N_GREATER8 : N_LESS8; + if (type_var & TYPE_SIGNED) { + if (final->type == N_NUM8 && final->value == 0x80 && (positive & 1) != 0) { + final->value = 0x7f; + comparison = N_EQUAL8; + } else { + comparison = (positive & 2) ? N_GREATER8S : N_LESS8S; + } + } else { + if (final->type == N_NUM8 && final->value == 0x00 && (positive & 1) != 0) { + final->value = 0xff; + comparison = N_EQUAL8; + } else { + comparison = (positive & 2) ? N_GREATER8 : N_LESS8; + } + } } final = node_create(comparison, 0, var, final); } @@ -2464,7 +2652,7 @@ void compile_statement(int check_for_else) new_loop = malloc(sizeof(struct loop)); if (new_loop == NULL) { fprintf(stderr, "Out of memory\n"); - exit(1); + exit(EXIT_FAILURE); } new_loop->type = NESTED_WHILE; new_loop->step = NULL; @@ -2505,7 +2693,7 @@ void compile_statement(int check_for_else) new_loop = malloc(sizeof(struct loop)); if (new_loop == NULL) { fprintf(stderr, "Out of memory\n"); - exit(1); + exit(EXIT_FAILURE); } if (lex == C_NAME && strcmp(name, "WHILE") == 0) { get_lex(); @@ -2578,7 +2766,6 @@ void compile_statement(int check_for_else) emit_error("bad nested LOOP"); } } else if (strcmp(name, "SELECT") == 0) { - int label_loop; int label_exit; int type; struct loop *new_loop; @@ -2589,7 +2776,7 @@ void compile_statement(int check_for_else) new_loop = malloc(sizeof(struct loop)); if (new_loop == NULL) { fprintf(stderr, "Out of memory\n"); - exit(1); + exit(EXIT_FAILURE); } new_loop->type = NESTED_SELECT; if (lex == C_NAME && strcmp(name, "CASE") == 0) { @@ -2657,6 +2844,7 @@ void compile_statement(int check_for_else) } else { min = tree->value; } + max = min; node_delete(tree); if (lex == C_NAME && strcmp(name, "TO") == 0) { get_lex(); @@ -2675,8 +2863,6 @@ void compile_statement(int check_for_else) max = tree->value; } node_delete(tree); - } else { - max = min; } if (loops->var[0] == (TYPE_8 | TYPE_SIGNED)) { min ^= 0x80; @@ -2753,122 +2939,17 @@ void compile_statement(int check_for_else) get_lex(); } } - } else if (strcmp(name, "POKE") == 0) { - struct node *address; - struct node *value; - + } else if (strcmp(name, "REM") == 0) { + line_pos = line_size; get_lex(); - address = evaluate_save_expression(1, TYPE_16); - if (lex == C_COMMA) - get_lex(); - else - emit_error("missing comma in POKE"); - value = evaluate_save_expression(1, TYPE_8); - if (target == CPU_6502) { - node_generate(value, 0); - cpu6502_noop("PHA"); - node_generate(address, 0); - cpu6502_1op("STA", "temp"); - cpu6502_1op("STY", "temp+1"); - cpu6502_noop("PLA"); - cpu6502_1op("LDY", "#0"); - cpu6502_1op("STA", "(temp),Y"); - } else if (target == CPU_9900) { - node_generate(value, 0); - cpu9900_1op("dect", "r10"); - cpu9900_2op("mov", "r0", "*r10"); - node_generate(address, 0); - cpu9900_2op("mov", "*r10+", "r1"); - cpu9900_2op("movb","r1","*r0"); + } else if (strcmp(name, "RESTORE") == 0) { + get_lex(); + if (lex != C_NAME) { + emit_error("bad syntax for RESTORE"); } else { - if ((value->regs & REG_HL) == 0) { - node_generate(address, 0); - node_generate(value, 0); - } else if ((address->regs & REG_A) == 0) { - node_generate(value, 0); - node_generate(address, 0); - } else { - node_generate(address, 0); - cpuz80_1op("PUSH", "HL"); - node_generate(value, 0); - cpuz80_1op("POP", "HL"); - } - cpuz80_2op("LD", "(HL)", "A"); - } - node_delete(address); - node_delete(value); - } else if (strcmp(name, "VPOKE") == 0) { - struct node *address; - struct node *value; - - get_lex(); - address = evaluate_save_expression(1, TYPE_16); - if (lex == C_COMMA) - get_lex(); - else - emit_error("missing comma in VPOKE"); - value = evaluate_save_expression(1, TYPE_8); - if (target == CPU_6502) { - node_generate(value, 0); - cpu6502_noop("PHA"); - node_generate(address, 0); - cpu6502_1op("STA", "temp"); - cpu6502_1op("STY", "temp+1"); - cpu6502_noop("PLA"); - cpu6502_noop("TAX"); - cpu6502_1op("LDA", "temp"); - cpu6502_noop("SEI"); - cpu6502_1op("JSR", "WRTVRM"); - cpu6502_noop("CLI"); - } else if (target == CPU_9900) { - node_generate(value, 0); - cpu9900_1op("dect", "r10"); - cpu9900_2op("mov", "r0", "*r10"); - node_generate(address, 0); - cpu9900_2op("mov", "*r10+", "r2"); - cpu9900_1op("limi","0"); - cpu9900_1op("bl","@JSR"); - cpu9900_1op("data", "WRTVRM"); - cpu9900_1op("limi","2"); - } else { - if ((value->regs & REG_HL) == 0) { - node_generate(address, 0); - node_generate(value, 0); - } else if ((address->regs & REG_A) == 0) { - node_generate(value, 0); - node_generate(address, 0); - } else { - node_generate(address, 0); - cpuz80_1op("PUSH", "HL"); - node_generate(value, 0); - cpuz80_1op("POP", "HL"); - } - cpuz80_1op("CALL", "NMI_OFF"); - cpuz80_1op("CALL", "WRTVRM"); - cpuz80_1op("CALL", "NMI_ON"); - } - node_delete(address); - node_delete(value); - } else if (strcmp(name, "REM") == 0) { - line_pos = line_size; - get_lex(); - } else if (strcmp(name, "CLS") == 0) { - get_lex(); - generic_call("cls"); - } else if (strcmp(name, "WAIT") == 0) { - get_lex(); - if (machine == SORD || machine == CREATIVISION || machine == EINSTEIN || machine == TI994A) - generic_call("wait"); - else - cpuz80_noop("HALT"); - } else if (strcmp(name, "RESTORE") == 0) { - get_lex(); - if (lex != C_NAME) { - emit_error("bad syntax for RESTORE"); - } else { - label = label_search(name); - if (label == NULL) { - label = label_add(name); + label = label_search(name); + if (label == NULL) { + label = label_add(name); } if (target == CPU_6502) { sprintf(temp, "#" LABEL_PREFIX "%s", name); @@ -2907,7 +2988,7 @@ void compile_statement(int check_for_else) } else if (strcmp(name, "DATA") == 0) { struct node *tree; int c = 0; - + generic_dump(); get_lex(); if (lex == C_NAME && strcmp(name, "BYTE") == 0) { @@ -2982,6 +3063,7 @@ void compile_statement(int check_for_else) int type2; get_lex(); + check_for_macro(); if (lex != C_NAME) { emit_error("missing variable name for VARPTR"); } else { @@ -3136,104 +3218,718 @@ void compile_statement(int check_for_else) fprintf(output, "\n"); } } - } else if (strcmp(name, "OUT") == 0) { - struct node *port; - struct node *value; + } else if (strcmp(name, "SIGNED") == 0) { + struct signedness *c; get_lex(); - port = evaluate_save_expression(1, TYPE_8); - if (lex == C_COMMA) + while (1) { + if (lex != C_NAME) { + emit_error("missing name in SIGNED"); + break; + } + c = signed_search(name); + if (c != NULL) { + emit_error("variable already SIGNED/UNSIGNED"); + } else { + c = signed_add(name); + } + c->sign = 1; get_lex(); - else - emit_error("missing comma in OUT"); - value = evaluate_save_expression(1, TYPE_8); - if (target == CPU_6502) { - emit_warning("Ignoring OUT (not supported in target)"); - } else if (target == CPU_9900) { - /* - ** We don't have ports (though we could map this to CRU) - ** however, since it seems OUT is the CVBasic way to directly - ** access the sound chip, we'll check for OUT $FF and map that - ** over. - */ - if (port->type == N_NUM8 && port->value == 0xff) { - node_generate(value, 0); - cpu9900_2op("movb", "r0", "@SOUND"); + if (lex != C_COMMA) + break; + get_lex(); + } + } else if (strcmp(name, "UNSIGNED") == 0) { + struct signedness *c; + + get_lex(); + while (1) { + if (lex != C_NAME) { + emit_error("missing name in UNSIGNED"); + break; + } + c = signed_search(name); + if (c != NULL) { + emit_error("variable already SIGNED/UNSIGNED"); } else { - emit_warning("OUT to 0xff for audio is the only supported use."); + c = signed_add(name); } + c->sign = 2; + get_lex(); + if (lex != C_COMMA) + break; + get_lex(); + } + } else if (strcmp(name, "CONST") == 0) { + struct constant *c; + + get_lex(); + if (lex != C_NAME) { + emit_error("name required for constant assignment"); + return; + } + c = constant_search(name); + if (c != NULL) { + emit_error("constant redefined"); } else { - node_generate(port, 0); - cpuz80_2op("LD", "C", "A"); - if ((value->regs & REG_C) == 0) { - node_generate(value, 0); + c = constant_add(name); + } + strcpy(assigned, name);; + get_lex(); + if (lex != C_EQUAL) { + emit_error("required '=' for constant assignment"); + } else { + struct node *tree; + int type; + + get_lex(); + tree = evaluate_level_0(&type); + if (tree->type != N_NUM8 && tree->type != N_NUM16) { + emit_error("not a constant expression in CONST"); } else { - cpuz80_1op("PUSH", "BC"); - node_generate(value, 0); - cpuz80_1op("POP", "BC"); + c->value = tree->value; } - cpuz80_2op("OUT", "(C)", "A"); + if (target == CPU_Z80 || target == CPU_6502) { + sprintf(temp, CONST_PREFIX "%s:\tequ $%04x", assigned, c->value); + fprintf(output, "%s\n", temp); + } else if (target == CPU_9900) { + sprintf(temp, CONST_PREFIX "%s\tequ >%04x", assigned, c->value); + cpu9900_label(temp); /* Hack */ + } + node_delete(tree); + tree = NULL; } - node_delete(port); - node_delete(value); - } else if (strcmp(name, "PRINT") == 0) { - int label; - int label2; - int c; - int start; - int cursor_value; - int cursor_pos; + } else if (strcmp(name, "DIM") == 0) { + char array[MAX_LINE_SIZE]; + struct label *new_array; struct node *tree; + int type; + int c; - get_lex(); - start = 1; - cursor_value = 0; - cursor_pos = 0; - if (lex == C_NAME && strcmp(name, "AT") == 0) { + while (1) { get_lex(); - tree = evaluate_save_expression(1, TYPE_16); - if (target == CPU_6502) { - if (tree->type == N_NUM16) { - cursor_value = 2; - cursor_pos = tree->value; + if (lex != C_NAME) { + emit_error("missing name in DIM"); + break; + } + strcpy(array, name); + get_lex(); + if (lex == C_LPAREN) { + get_lex(); + tree = evaluate_level_0(&type); + if (tree->type != N_NUM8 && tree->type != N_NUM16) { + emit_error("not a constant expression in DIM"); + c = 10; } else { - node_generate(tree, 0); - cursor_value = 1; + c = tree->value; + } + node_delete(tree); + if (lex != C_RPAREN) { + emit_error("missing right parenthesis in DIM"); + } else { + get_lex(); + } + new_array = array_search(array); + if (new_array != NULL) { + emit_error("array already defined"); + } else { + new_array = array_add(array); + new_array->length = c; } - } else if (target == CPU_9900) { - node_generate(tree, 0); - cpu9900_2op("mov", "r0", "@cursor"); } else { - node_generate(tree, 0); - cpuz80_2op("LD", "(cursor)", "HL"); + label = label_add(array); + if (array[0] == '#') + label->used = TYPE_16; + else + label->used = TYPE_8; + label->used |= LABEL_IS_VARIABLE; } - start = 0; + if (lex != C_COMMA) + break; } - while (1) { - if (!start) { - if (lex != C_COMMA) { - if (target == CPU_6502) { - if (cursor_value) { - if (cursor_value == 2) { - sprintf(temp, "#%d", cursor_pos & 0xff); - cpu6502_1op("LDA", temp); - sprintf(temp, "#%d", cursor_pos >> 8); - cpu6502_1op("LDY", temp); - } - cpu6502_1op("STA", "cursor"); - cpu6502_1op("STY", "cursor+1"); - } - } - break; - } - get_lex(); - } - start = 0; - if (lex == C_STRING) { - if (name_size) { - if (target == CPU_6502) { - if (cursor_value) { - if (cursor_value == 2) { + } else if (strcmp(name, "ON") == 0) { + struct label *label; + int table; + int c; + int max_value; + int gosub; + int fast; + struct label *options[256]; + int new_label; + int type; + + get_lex(); + if (lex == C_NAME && strcmp(name, "FRAME") == 0) { /* Frame-driven games */ + get_lex(); + if (lex != C_NAME || strcmp(name, "GOSUB") != 0) { + emit_error("Bad syntax for ON FRAME GOSUB"); + } + get_lex(); + if (lex != C_NAME) { + emit_error("Missing label for ON FRAME GOSUB"); + } + if (frame_drive != NULL) { + emit_error("More than one ON FRAME GOSUB"); + } + label = label_search(name); + if (label == NULL) { + label = label_add(name); + } + label->used |= LABEL_USED; + label->used |= LABEL_CALLED_BY_GOSUB; + frame_drive = label; + get_lex(); + } else { + type = evaluate_expression(0, 0, 0); + fast = 0; + if (lex == C_NAME && strcmp(name, "FAST") == 0) { + get_lex(); + fast = 1; + } + gosub = 0; + if (lex != C_NAME || (strcmp(name, "GOTO") != 0 && strcmp(name, "GOSUB") != 0)) { + emit_error("required GOTO or GOSUB after ON"); + } else if (strcmp(name, "GOTO") == 0) { + get_lex(); + } else if (strcmp(name, "GOSUB") == 0) { + get_lex(); + gosub = 1; + } + max_value = 0; + while (1) { + if (max_value == sizeof(options) / sizeof(struct label *)) { + emit_error("too many options for ON statement"); + max_value--; + } + if (lex == C_NAME) { + label = label_search(name); + if (label == NULL) { + label = label_add(name); + } + label->used |= LABEL_USED; + if (gosub != 0) { + label->used |= LABEL_CALLED_BY_GOSUB; + } else { + label->used |= LABEL_CALLED_BY_GOTO; + } + options[max_value++] = label; + get_lex(); + } else { + options[max_value++] = NULL; + } + if (lex != C_COMMA) + break; + get_lex(); + } + table = next_local++; + new_label = next_local++; + if (fast == 0) { + if (target == CPU_6502) { + if ((type & MAIN_TYPE) == TYPE_8) { + cpu6502_1op("STA", "temp"); + cpu6502_1op("LDY", "#0"); + cpu6502_1op("STY", "temp+1"); + sprintf(temp, "#%d", max_value); + cpu6502_1op("CMP", temp); + } else { + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); + sprintf(temp, "#%d", max_value); + cpu6502_1op("LDA", "temp"); + cpu6502_noop("SEC"); + cpu6502_1op("SBC", temp); + cpu6502_1op("LDA", "temp+1"); + strcat(temp, ">>8"); + cpu6502_1op("SBC", temp); + } + sprintf(temp, INTERNAL_PREFIX "%d", new_label); + cpu6502_1op("BCS.L", temp); + } else if (target == CPU_9900) { + if ((type & MAIN_TYPE) == TYPE_8) { + sprintf(temp, "%d ; %d*256", max_value*256, max_value); + cpu9900_2op("ci", "r0", temp); + } else { + sprintf(temp, "%d", max_value); + cpu9900_2op("ci", "r0", temp); + } + sprintf(temp, "@" INTERNAL_PREFIX "%d", new_label); + sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); + cpu9900_1op("jl", temp + 100); + cpu9900_1op("b", temp); + cpu9900_label(temp + 100); + } else { + if ((type & MAIN_TYPE) == TYPE_8) { + sprintf(temp, "%d", max_value); + cpuz80_1op("CP", temp); + } else { + sprintf(temp, "%d", max_value); + cpuz80_2op("LD", "DE", temp); + cpuz80_1op("OR", "A"); + cpuz80_2op("SBC", "HL", "DE"); + cpuz80_2op("ADD", "HL", "DE"); + } + sprintf(temp, INTERNAL_PREFIX "%d", new_label); + cpuz80_2op("JP", "NC", temp); + } + } + if (gosub) { + if (target == CPU_6502) { + sprintf(temp, "#(" INTERNAL_PREFIX "%d-1)>>8", new_label); + cpu6502_1op("LDA", temp); + cpu6502_noop("PHA"); + sprintf(temp, "#" INTERNAL_PREFIX "%d-1", new_label); + cpu6502_1op("LDA", temp); + cpu6502_noop("PHA"); + } else if (target == CPU_9900) { + sprintf(temp, INTERNAL_PREFIX "%d", new_label); + cpu9900_2op("li", "r1", temp); + cpu9900_1op("dect", "r10"); /* stack manipulation */ + cpu9900_2op("mov", "r1", "*r10"); + } else { + sprintf(temp, INTERNAL_PREFIX "%d", new_label); + cpuz80_2op("LD", "DE", temp); + cpuz80_1op("PUSH", "DE"); + } + } + if (target == CPU_6502) { + cpu6502_1op("LDA", "temp"); + cpu6502_1op("ASL", "A"); + cpu6502_1op("ROL", "temp+1"); + cpu6502_noop("CLC"); + sprintf(temp, "#" INTERNAL_PREFIX "%d", table); + cpu6502_1op("ADC", temp); + cpu6502_1op("STA", "temp"); + cpu6502_1op("LDA", "temp+1"); + strcat(temp, ">>8"); + cpu6502_1op("ADC", temp); + cpu6502_1op("STA", "temp+1"); + cpu6502_1op("LDY", "#0"); + cpu6502_1op("LDA", "(temp),Y"); + cpu6502_1op("STA", "temp2"); + cpu6502_noop("INY"); + cpu6502_1op("LDA", "(temp),Y"); + cpu6502_1op("STA", "temp2+1"); + cpu6502_1op("JMP", "(temp2)"); + } else if (target == CPU_9900) { + if ((type & MAIN_TYPE) == TYPE_8) { + cpu9900_2op("srl", "r0", "8"); + } + cpu9900_2op("sla", "r0", "1"); + cpu9900_2op("mov", "r0", "r1"); + sprintf(temp, "@" INTERNAL_PREFIX "%d(r1)", table); + cpu9900_2op("mov", temp, "r0"); + cpu9900_1op("b", "*r0"); + } else { + if ((type & MAIN_TYPE) == TYPE_8) { + cpuz80_2op("LD", "L", "A"); + if (type & TYPE_SIGNED) { + cpuz80_noop("RLA"); + cpuz80_2op("SBC", "A", "A"); + cpuz80_2op("LD", "H", "A"); + } else { + cpuz80_2op("LD", "H", "0"); + } + } + cpuz80_2op("ADD", "HL", "HL"); + sprintf(temp, INTERNAL_PREFIX "%d", table); + cpuz80_2op("LD", "DE", temp); + cpuz80_2op("ADD", "HL", "DE"); + cpuz80_2op("LD", "A", "(HL)"); + cpuz80_1op("INC", "HL"); + cpuz80_2op("LD", "H", "(HL)"); + cpuz80_2op("LD", "L", "A"); + cpuz80_1op("JP", "(HL)"); + } + sprintf(temp, INTERNAL_PREFIX "%d", table); + generic_label(temp); + for (c = 0; c < max_value; c++) { + if (options[c] != NULL) { + sprintf(temp, LABEL_PREFIX "%s", options[c]->name); + } else { + sprintf(temp, INTERNAL_PREFIX "%d", new_label); + } + if (target == CPU_6502) + cpu6502_1op("DW", temp); + else if (target == CPU_9900) + cpu9900_1op("data", temp); + else + cpuz80_1op("DW", temp); + } + sprintf(temp, INTERNAL_PREFIX "%d", new_label); + generic_label(temp); + } + } else if (strcmp(name, "OPTION") == 0) { + get_lex(); + if (lex != C_NAME) { + emit_error("required name after OPTION"); + } else if (strcmp(name, "EXPLICIT") == 0) { + get_lex(); + if (lex == C_NAME && strcmp(name, "ON") == 0) { + get_lex(); + option_explicit = 1; + } else if (lex == C_NAME && strcmp(name, "OFF") == 0) { + get_lex(); + option_explicit = 0; + } else { + option_explicit = 1; + } + } else if (strcmp(name, "WARNINGS") == 0) { + get_lex(); + if (lex == C_NAME && strcmp(name, "ON") == 0) { + get_lex(); + option_warnings = 1; + } else if (lex == C_NAME && strcmp(name, "OFF") == 0) { + get_lex(); + option_warnings = 0; + } else { + emit_error("missing ON/OFF in OPTION WARNINGS"); + } + } else if (strcmp(name, "FM") == 0) { + if (machine != MSX && machine != MSX2 && machine != SMS) + emit_warning("OPTION FM only works for MSX/MSX2/SMS"); + get_lex(); + if (lex == C_NAME && strcmp(name, "ON") == 0) { + get_lex(); + option_fm = 1; + } else if (lex == C_NAME && strcmp(name, "OFF") == 0) { + get_lex(); + option_fm = 0; + } else { + emit_error("missing ON/OFF in OPTION FM"); + } + } else { + emit_error("non-recognized OPTION"); + } + } else if (strcmp(name, "DEF") == 0) { /* Function definition (macro in CVBasic) */ + char function[MAX_LINE_SIZE]; + int total_arguments; + char *arguments[32]; + struct macro *macro; + int c; + + get_lex(); + if (lex != C_NAME || strcmp(name, "FN") != 0) { + emit_error("syntax error for DEF FN"); + } else { + get_lex(); + if (lex != C_NAME) { + emit_error("missing function name for DEF FN"); + } else if (macro_search(name) != NULL) { + emit_error("DEF FN name already defined"); + } else { + strcpy(function, name); + get_lex(); + total_arguments = 0; + if (lex == C_LPAREN) { + get_lex(); + while (1) { + if (lex != C_NAME) { + emit_error("syntax error in argument list for DEF FN"); + break; + } + if (total_arguments == 32) { + emit_error("More than 32 arguments in DEF FN"); + break; + } + arguments[total_arguments] = malloc(strlen(name) + 1); + if (arguments[total_arguments] == NULL) { + emit_error("Out of memory in DEF FN"); + break; + } + strcpy(arguments[total_arguments], name); + total_arguments++; + get_lex(); + if (lex == C_COMMA) { + get_lex(); + } else if (lex == C_RPAREN) { + get_lex(); + break; + } else { + emit_error("syntax error in argument list for DEF FN"); + break; + } + } + } + macro = macro_add(function); + macro->total_arguments = total_arguments; + if (lex != C_EQUAL) { + emit_error("missing = in DEF FN"); + } else { + get_lex(); + while (lex != C_END) { + if (lex == C_ERR) { + emit_error("bad syntax inside DEF FN replacement text"); + break; + } + if (lex == C_NAME) { + for (c = 0; c < total_arguments; c++) { + if (strcmp(arguments[c], name) == 0) + break; + } + if (c < total_arguments) { + lex = C_ERR; + value = c; + name[0] = '\0'; + } + } + if (macro->length >= macro->max_length) { + macro->definition = realloc(macro->definition, (macro->max_length + 1) * 2 * sizeof(struct macro_def)); + if (macro->definition == NULL) { + emit_error("Out of memory in DEF FN"); + break; + } + macro->max_length = (macro->max_length + 1) * 2; + } + macro->definition[macro->length].lex = lex; + if (lex == C_STRING) + value = name_size; + macro->definition[macro->length].value = value; + if (lex == C_STRING) + macro->definition[macro->length].name = malloc(name_size + 1); + else + macro->definition[macro->length].name = malloc(strlen(name) + 1); + if (macro->definition[macro->length].name == NULL) { + emit_error("Out of memory in DEF FN"); + break; + } + if (lex == C_STRING) { + if (value) + memcpy(macro->definition[macro->length].name, name, value); + } else { + strcpy(macro->definition[macro->length].name, name); + } + macro->length++; + get_lex(); + } + } + } + } + + /* + ** Non-portable language + */ + } else if (strcmp(name, "POKE") == 0) { + struct node *address; + struct node *value; + + get_lex(); + address = evaluate_save_expression(1, TYPE_16); + if (lex == C_COMMA) + get_lex(); + else + emit_error("missing comma in POKE"); + value = evaluate_save_expression(1, TYPE_8); + if (target == CPU_6502) { + node_generate(value, 0); + cpu6502_noop("PHA"); + node_generate(address, 0); + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); + cpu6502_noop("PLA"); + cpu6502_1op("LDY", "#0"); + cpu6502_1op("STA", "(temp),Y"); + } else if (target == CPU_9900) { + node_generate(value, 0); + cpu9900_1op("dect", "r10"); + cpu9900_2op("mov", "r0", "*r10"); + node_generate(address, 0); + cpu9900_2op("mov", "*r10+", "r1"); + cpu9900_2op("movb","r1","*r0"); + } else { + if ((value->regs & REG_HL) == 0) { + node_generate(address, 0); + node_generate(value, 0); + } else if ((address->regs & REG_A) == 0) { + node_generate(value, 0); + node_generate(address, 0); + } else { + node_generate(address, 0); + cpuz80_1op("PUSH", "HL"); + node_generate(value, 0); + cpuz80_1op("POP", "HL"); + } + cpuz80_2op("LD", "(HL)", "A"); + } + node_delete(address); + node_delete(value); + } else if (strcmp(name, "VPOKE") == 0) { + struct node *address; + struct node *value; + + get_lex(); + address = evaluate_save_expression(1, TYPE_16); + if (lex == C_COMMA) + get_lex(); + else + emit_error("missing comma in VPOKE"); + value = evaluate_save_expression(1, TYPE_8); + if (target == CPU_6502) { + if (machine == NES) { + node_generate(value, 0); + cpu6502_noop("PHA"); + node_generate(address, 0); + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); + cpu6502_noop("PLA"); + cpu6502_noop("TAX"); + cpu6502_1op("LDA", "temp"); + cpu6502_1op("JSR", "WRTVRM"); + } else { + node_generate(value, 0); + cpu6502_noop("PHA"); + node_generate(address, 0); + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); + cpu6502_noop("PLA"); + cpu6502_noop("TAX"); + cpu6502_1op("LDA", "temp"); + generic_interrupt_disable(); + cpu6502_1op("JSR", "WRTVRM"); + generic_interrupt_enable(); + } + } else if (target == CPU_9900) { + node_generate(value, 0); + cpu9900_1op("dect", "r10"); + cpu9900_2op("mov", "r0", "*r10"); + node_generate(address, 0); + cpu9900_2op("mov", "*r10+", "r2"); + generic_interrupt_disable(); + cpu9900_1op("bl","@JSR"); + cpu9900_1op("data", "WRTVRM"); + generic_interrupt_enable(); + } else { + if ((value->regs & REG_HL) == 0) { + node_generate(address, 0); + node_generate(value, 0); + } else if ((address->regs & REG_A) == 0) { + node_generate(value, 0); + node_generate(address, 0); + } else { + node_generate(address, 0); + cpuz80_1op("PUSH", "HL"); + node_generate(value, 0); + cpuz80_1op("POP", "HL"); + } + generic_interrupt_disable(); + cpuz80_1op("CALL", "WRTVRM"); + generic_interrupt_enable(); + } + node_delete(address); + node_delete(value); + } else if (strcmp(name, "CLS") == 0) { + get_lex(); + generic_call("cls"); + } else if (strcmp(name, "WAIT") == 0) { + get_lex(); + if (machine == SORD || machine == EINSTEIN || machine == CREATIVISION || machine == NES || machine == TI994A) { + generic_call("wait"); + } else if (machine == COLECOVISION || machine == COLECOVISION_SGM) { + cpuz80_noop("DB $76+COLECO_SPINNER*$59"); /* HALT or RST $08 */ + } else { + cpuz80_noop("HALT"); + } + } else if (strcmp(name, "OUT") == 0) { + struct node *port; + struct node *value; + + get_lex(); + port = evaluate_save_expression(1, TYPE_8); + if (lex == C_COMMA) + get_lex(); + else + emit_error("missing comma in OUT"); + value = evaluate_save_expression(1, TYPE_8); + if (target == CPU_6502) { + emit_warning("Ignoring OUT (not supported for 6502)"); + } else if (target == CPU_9900) { + /* + ** We don't have ports (though we could map this to CRU) + ** however, since it seems OUT is the CVBasic way to directly + ** access the sound chip, we'll check for OUT $FF and map that + ** over. + */ + if (port->type == N_NUM8 && port->value == 0xff) { + node_generate(value, 0); + cpu9900_2op("movb", "r0", "@SOUND"); + } else { + emit_warning("OUT to $ff for audio is the only supported use."); + } + } else { + node_generate(port, 0); + cpuz80_2op("LD", "C", "A"); + if ((value->regs & REG_C) == 0) { + node_generate(value, 0); + } else { + cpuz80_1op("PUSH", "BC"); + node_generate(value, 0); + cpuz80_1op("POP", "BC"); + } + cpuz80_2op("OUT", "(C)", "A"); + } + node_delete(port); + node_delete(value); + } else if (strcmp(name, "PRINT") == 0) { + int label; + int label2; + int c; + int start; + int cursor_value; + int cursor_pos; + struct node *tree; + + get_lex(); + label2 = 0; + start = 1; + cursor_value = 0; + cursor_pos = 0; + if (lex == C_NAME && strcmp(name, "AT") == 0) { + get_lex(); + tree = evaluate_save_expression(1, TYPE_16); + if (machine == SMS) { + tree = node_create(N_MUL16, 0, tree, node_create(N_NUM16, 2, NULL, NULL)); + } + if (target == CPU_6502) { + if (tree->type == N_NUM16) { + cursor_value = 2; + cursor_pos = tree->value; + } else { + node_generate(tree, 0); + cursor_value = 1; + } + } else if (target == CPU_9900) { + node_generate(tree, 0); + cpu9900_2op("mov", "r0", "@cursor"); + } else { + node_generate(tree, 0); + cpuz80_2op("LD", "(cursor)", "HL"); + } + start = 0; + } + while (1) { + if (!start) { + if (lex != C_COMMA) { + if (target == CPU_6502) { + if (cursor_value) { + if (cursor_value == 2) { + sprintf(temp, "#%d", cursor_pos & 0xff); + cpu6502_1op("LDA", temp); + sprintf(temp, "#%d", cursor_pos >> 8); + cpu6502_1op("LDY", temp); + } + cpu6502_1op("STA", "cursor"); + cpu6502_1op("STY", "cursor+1"); + } + } + break; + } + get_lex(); + } + start = 0; + if (lex == C_STRING) { + if (name_size) { + if (target == CPU_6502) { + if (cursor_value) { + if (cursor_value == 2) { generic_call("print_string_cursor_constant"); generic_dump(); fprintf(output, "\tDB $%02x,$%02x,$%02x\n", cursor_pos & 0xff, (cursor_pos >> 8) & 0xff, name_size); @@ -3269,7 +3965,10 @@ void compile_statement(int check_for_else) cpuz80_2op("LD", "A", temp); generic_call("print_string"); sprintf(temp, INTERNAL_PREFIX "%d", label2); - generic_jump(temp); + if (name_size < 126) + generic_jump_short(temp); + else + generic_jump(temp); sprintf(temp, INTERNAL_PREFIX "%d", label); generic_label(temp); generic_dump(); @@ -3371,7 +4070,7 @@ void compile_statement(int check_for_else) if (format == 0) { cpu6502_1op("JSR", "print_number"); } else if (format == 1) { - cpu6502_noop("SEI"); + generic_interrupt_disable(); cpu6502_1op("LDX", "#2"); cpu6502_1op("STX", "temp"); cpu6502_1op("LDX", "#32"); @@ -3379,7 +4078,7 @@ void compile_statement(int check_for_else) sprintf(temp, "print_number%d", size); cpu6502_1op("JSR", temp); } else if (format == 2) { - cpu6502_noop("SEI"); + generic_interrupt_disable(); cpu6502_1op("LDX", "#2"); cpu6502_1op("STX", "temp"); cpu6502_1op("LDX", "#48"); @@ -3393,13 +4092,13 @@ void compile_statement(int check_for_else) cpu9900_1op("bl", "@JSR"); cpu9900_1op("data", "print_number"); } else if (format == 1) { - cpu9900_1op("limi","0"); /* print_number will turn it back on */ + generic_interrupt_disable(); cpu9900_2op("li", "r5", ">0220"); sprintf(temp, "print_number%d", size); cpu9900_1op("bl", "@JSR"); cpu9900_1op("data", temp); } else if (format == 2) { - cpu9900_1op("limi","0"); /* print_number will turn it back on */ + generic_interrupt_disable(); cpu9900_2op("li", "r5", ">0230"); sprintf(temp, "print_number%d", size); cpu9900_1op("bl", "@JSR"); @@ -3409,12 +4108,12 @@ void compile_statement(int check_for_else) if (format == 0) { cpuz80_1op("CALL", "print_number"); } else if (format == 1) { - cpuz80_1op("CALL", "nmi_off"); + generic_interrupt_disable(); cpuz80_2op("LD", "BC", "$0220"); sprintf(temp, "print_number%d", size); cpuz80_1op("CALL", temp); } else if (format == 2) { - cpuz80_1op("CALL", "nmi_off"); + generic_interrupt_disable(); cpuz80_2op("LD", "BC", "$0230"); sprintf(temp, "print_number%d", size); cpuz80_1op("CALL", temp); @@ -3433,11 +4132,32 @@ void compile_statement(int check_for_else) cpu6502_1op("STY", "cursor+1"); } } - type = evaluate_expression(1, TYPE_16, 0); - if (target == CPU_9900) { - cpu9900_2op("mov", "r0", "r3"); + if (lex == C_NAME && strcmp(name, "CHR$") == 0) { + get_lex(); + if (lex != C_LPAREN) + emit_error("syntax error in CHR$"); + else + get_lex(); + type = evaluate_expression(1, TYPE_8, 0); + if (lex != C_RPAREN) + emit_error("syntax error in CHR$"); + else + get_lex(); + if (target == CPU_6502) { + cpu6502_noop("TAX"); + } else if (target == CPU_9900) { + cpu9900_2op("mov", "r0", "r2"); + cpu9900_1op("swpb", "r2"); + } + generic_interrupt_disable(); + generic_call("print_char"); + generic_interrupt_enable(); + } else { + type = evaluate_expression(1, TYPE_16, 0); + if (target == CPU_9900) + cpu9900_2op("mov", "r0", "r3"); + generic_call("print_number"); } - generic_call("print_number"); } cursor_value = 0; } @@ -3446,87 +4166,21 @@ void compile_statement(int check_for_else) int vram_read = 0; get_lex(); + if (machine == NES) { + emit_error("DEFINE isn't implemented for NES"); + break; + } if (lex != C_NAME) { emit_error("syntax error in DEFINE"); } else if (strcmp(name, "SPRITE") == 0) { get_lex(); - if (lex == C_NAME && strcmp(name, "PLETTER") == 0) { - pletter = 1; - get_lex(); - } - if (pletter) { - type = evaluate_expression(1, TYPE_16, 0); - if (target == CPU_6502) { - cpu6502_1op("ASL", "A"); - cpu6502_1op("ASL", "A"); - cpu6502_1op("LDY", "#7"); - cpu6502_1op("STY", "pointer+1"); - cpu6502_1op("ASL", "A"); - cpu6502_1op("ROL", "pointer+1"); - cpu6502_1op("ASL", "A"); - cpu6502_1op("ROL", "pointer+1"); - cpu6502_1op("ASL", "A"); - cpu6502_1op("ROL", "pointer+1"); - cpu6502_1op("STA", "pointer"); - } else if (target == CPU_9900) { - cpu9900_2op("mov","r0","r4"); - cpu9900_2op("sla","r4","5"); - cpu9900_2op("ai","r4",">3800"); - } else { - cpuz80_2op("ADD", "HL", "HL"); - cpuz80_2op("ADD", "HL", "HL"); - cpuz80_2op("LD", "H", "$07"); - cpuz80_2op("ADD", "HL", "HL"); - cpuz80_2op("ADD", "HL", "HL"); - cpuz80_2op("ADD", "HL", "HL"); - cpuz80_2op("EX", "DE", "HL"); - } - if (lex == C_COMMA) - get_lex(); - else - emit_error("missing comma in DEFINE"); - type = evaluate_expression(2, TYPE_8, 0); - if (lex == C_COMMA) - get_lex(); - else - emit_error("missing comma in DEFINE"); - if (lex != C_NAME) { - emit_error("missing label in DEFINE"); - } else { - if (target == CPU_6502) { - strcpy(temp, "#" LABEL_PREFIX); - strcat(temp, name); - cpu6502_1op("LDA", temp); - strcat(temp, ">>8"); - cpu6502_1op("LDY", temp); - cpu6502_1op("STA", "temp"); - cpu6502_1op("STY", "temp+1"); - } else if (target == CPU_9900) { - strcpy(temp, LABEL_PREFIX); - strcat(temp, name); - cpu9900_2op("li", "r2", temp); - cpu9900_2op("mov","r4","r1"); - } else { - strcpy(temp, LABEL_PREFIX); - strcat(temp, name); - cpuz80_2op("LD", "HL", temp); - } - get_lex(); - } - generic_call("unpack"); - compression_used = 1; - } else { + if (lex == C_NAME && strcmp(name, "COLOR") == 0 && machine == MSX2) { struct node *length; struct node *source = NULL; - type = evaluate_expression(1, TYPE_16, 0); /* char number */ - if (target == CPU_6502) { - cpu6502_1op("STA", "pointer"); - } else if (target == CPU_9900) { - cpu9900_2op("mov","r0","r4"); - } else { - cpuz80_1op("PUSH", "HL"); - } + get_lex(); + type = evaluate_expression(1, TYPE_16, 0); /* sprite number */ + cpuz80_1op("PUSH", "HL"); if (lex == C_COMMA) get_lex(); else @@ -3541,49 +4195,162 @@ void compile_statement(int check_for_else) } else if (strcmp(name, "VARPTR") == 0) { source = evaluate_save_expression(1, TYPE_16); /* CPU address (variable) */ node_generate(length, 0); + if ((source->regs & REG_A) != 0) + cpuz80_1op("PUSH", "AF"); + node_generate(source, 0); + if ((source->regs & REG_A) != 0) + cpuz80_1op("POP", "AF"); + } else { + node_generate(length, 0); /* CPU address (immediate) */ + strcpy(temp, LABEL_PREFIX); + strcat(temp, name); + cpuz80_2op("LD", "HL", temp); + get_lex(); + } + generic_call("define_sprite_color"); + node_delete(length); + node_delete(source); + } else { + if (lex == C_NAME && strcmp(name, "PLETTER") == 0) { + pletter = 1; + get_lex(); + } + if (pletter) { + type = evaluate_expression(1, TYPE_16, 0); if (target == CPU_6502) { - cpu6502_noop("PHA"); - node_generate(source, 0); - cpu6502_1op("STA", "temp"); - cpu6502_1op("STY", "temp+1"); - cpu6502_noop("PLA"); + cpu6502_1op("ASL", "A"); + cpu6502_1op("ASL", "A"); + cpu6502_1op("LDY", "#7"); + cpu6502_1op("STY", "pointer+1"); + cpu6502_1op("ASL", "A"); + cpu6502_1op("ROL", "pointer+1"); + cpu6502_1op("ASL", "A"); + cpu6502_1op("ROL", "pointer+1"); + cpu6502_1op("ASL", "A"); + cpu6502_1op("ROL", "pointer+1"); + cpu6502_1op("STA", "pointer"); } else if (target == CPU_9900) { - cpu9900_2op("mov","r0","r5"); - node_generate(source, 0); + cpu9900_2op("mov","r0","r4"); + cpu9900_2op("sla","r4","5"); + cpu9900_2op("ai","r4",">3800"); } else { - if ((source->regs & REG_A) != 0) - cpuz80_1op("PUSH", "AF"); - node_generate(source, 0); - if ((source->regs & REG_A) != 0) - cpuz80_1op("POP", "AF"); + cpuz80_2op("ADD", "HL", "HL"); + cpuz80_2op("ADD", "HL", "HL"); + if (machine == SMS) + cpuz80_2op("SET", "1", "H"); + else + cpuz80_2op("LD", "H", "$07"); + cpuz80_2op("ADD", "HL", "HL"); + cpuz80_2op("ADD", "HL", "HL"); + cpuz80_2op("ADD", "HL", "HL"); + if (machine == SMS) + cpuz80_2op("ADD", "HL", "HL"); + cpuz80_2op("EX", "DE", "HL"); + } + if (lex == C_COMMA) + get_lex(); + else + emit_error("missing comma in DEFINE"); + type = evaluate_expression(2, TYPE_8, 0); + if (lex == C_COMMA) + get_lex(); + else + emit_error("missing comma in DEFINE"); + if (lex != C_NAME) { + emit_error("missing label in DEFINE"); + } else { + if (target == CPU_6502) { + strcpy(temp, "#" LABEL_PREFIX); + strcat(temp, name); + cpu6502_1op("LDA", temp); + strcat(temp, ">>8"); + cpu6502_1op("LDY", temp); + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); + } else if (target == CPU_9900) { + strcpy(temp, LABEL_PREFIX); + strcat(temp, name); + cpu9900_2op("li", "r2", temp); + cpu9900_2op("mov","r4","r1"); + } else { + strcpy(temp, LABEL_PREFIX); + strcat(temp, name); + cpuz80_2op("LD", "HL", temp); + } + get_lex(); } + generic_call("unpack"); + compression_used = 1; } else { - node_generate(length, 0); /* CPU address (immediate) */ + struct node *length; + struct node *source = NULL; + + type = evaluate_expression(1, TYPE_16, 0); /* sprite number */ if (target == CPU_6502) { - cpu6502_noop("PHA"); - strcpy(temp, "#" LABEL_PREFIX); - strcat(temp, name); - cpu6502_1op("LDA", temp); - cpu6502_1op("STA", "temp"); - strcat(temp, ">>8"); - cpu6502_1op("LDA", temp); - cpu6502_1op("STA", "temp+1"); - cpu6502_noop("PLA"); + cpu6502_1op("STA", "pointer"); } else if (target == CPU_9900) { - cpu9900_2op("mov","r0","r5"); - strcpy(temp, LABEL_PREFIX); - strcat(temp, name); - cpu9900_2op("li","r0",temp); + cpu9900_2op("mov","r0","r4"); } else { - strcpy(temp, LABEL_PREFIX); - strcat(temp, name); - cpuz80_2op("LD", "HL", temp); + cpuz80_1op("PUSH", "HL"); } - get_lex(); + if (lex == C_COMMA) + get_lex(); + else + emit_error("missing comma in DEFINE"); + length = evaluate_save_expression(1, TYPE_8); /* count */ + if (lex == C_COMMA) + get_lex(); + else + emit_error("missing comma in DEFINE"); + if (lex != C_NAME) { + emit_error("missing label in DEFINE"); + } else if (strcmp(name, "VARPTR") == 0) { + source = evaluate_save_expression(1, TYPE_16); /* CPU address (variable) */ + node_generate(length, 0); + if (target == CPU_6502) { + cpu6502_noop("PHA"); + node_generate(source, 0); + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); + cpu6502_noop("PLA"); + } else if (target == CPU_9900) { + cpu9900_2op("mov","r0","r5"); + node_generate(source, 0); + } else { + if ((source->regs & REG_A) != 0) + cpuz80_1op("PUSH", "AF"); + node_generate(source, 0); + if ((source->regs & REG_A) != 0) + cpuz80_1op("POP", "AF"); + } + } else { + node_generate(length, 0); /* CPU address (immediate) */ + if (target == CPU_6502) { + cpu6502_noop("PHA"); + strcpy(temp, "#" LABEL_PREFIX); + strcat(temp, name); + cpu6502_1op("LDA", temp); + cpu6502_1op("STA", "temp"); + strcat(temp, ">>8"); + cpu6502_1op("LDA", temp); + cpu6502_1op("STA", "temp+1"); + cpu6502_noop("PLA"); + } else if (target == CPU_9900) { + cpu9900_2op("mov","r0","r5"); + strcpy(temp, LABEL_PREFIX); + strcat(temp, name); + cpu9900_2op("li","r0",temp); + } else { + strcpy(temp, LABEL_PREFIX); + strcat(temp, name); + cpuz80_2op("LD", "HL", temp); + } + get_lex(); + } + generic_call("define_sprite"); + node_delete(length); + node_delete(source); } - generic_call("define_sprite"); - node_delete(length); - node_delete(source); } } else if (strcmp(name, "CHAR") == 0 || strcmp(name, "COLOR") == 0) { int color; @@ -3808,27 +4575,18 @@ void compile_statement(int check_for_else) generic_call("unpack"); compression_used = 1; } else { - if (target == CPU_6502) { - cpu6502_noop("SEI"); - } else if (target == CPU_9900) { + if (target == CPU_9900) { cpu9900_2op("mov","r0","r2"); cpu9900_2op("mov","r5","r3"); cpu9900_2op("mov","r4","r0"); - cpu9900_1op("limi","0"); - } else { - cpuz80_1op("CALL", "nmi_off"); } + generic_interrupt_disable(); if (vram_read) { generic_call("LDIRMV"); } else { generic_call("LDIRVM"); } - if (target == CPU_6502) - cpu6502_noop("CLI"); - else if (target == CPU_9900) - cpu9900_1op("limi","2"); - else - cpuz80_1op("CALL", "nmi_on"); + generic_interrupt_enable(); } node_delete(length); node_delete(target2); @@ -3893,80 +4651,273 @@ void compile_statement(int check_for_else) else emit_error("missing comma in SPRITE"); type = evaluate_expression(1, TYPE_8, 0); - if (target == CPU_6502) - cpu6502_1op("STA", "sprite_data+1"); - else if (target == CPU_9900) + if (target == CPU_6502) { + if (machine == NES) + cpu6502_1op("STA", "sprite_data+3"); + else + cpu6502_1op("STA", "sprite_data+1"); + } else if (target == CPU_9900) { cpu9900_2op("movb","r0","r6"); - else - cpuz80_1op("PUSH", "AF"); - if (lex == C_COMMA) - get_lex(); - else - emit_error("missing comma in SPRITE"); - type = evaluate_expression(1, TYPE_8, 0); - if (target == CPU_6502) - cpu6502_1op("STA", "sprite_data+2"); - else if (target == CPU_9900) - cpu9900_2op("movb","r0","r7"); - else + } else { cpuz80_1op("PUSH", "AF"); + } if (lex == C_COMMA) get_lex(); else emit_error("missing comma in SPRITE"); type = evaluate_expression(1, TYPE_8, 0); - if (target == CPU_6502) { - cpu6502_1op("STA", "sprite_data+3"); - cpu6502_noop("PLA"); + if (machine == MSX2 && lex != C_COMMA) { + generic_call("update_sprite2"); + } else { + if (machine != SMS) { + if (target == CPU_6502) { + if (machine == NES) + cpu6502_1op("STA", "sprite_data+1"); + else + cpu6502_1op("STA", "sprite_data+2"); + } else if (target == CPU_9900) + cpu9900_2op("movb","r0","r7"); + else + cpuz80_1op("PUSH", "AF"); + if (lex == C_COMMA) + get_lex(); + else + emit_error("missing comma in SPRITE"); + type = evaluate_expression(1, TYPE_8, 0); + if (target == CPU_6502) { + if (machine == NES) + cpu6502_1op("STA", "sprite_data+2"); + else + cpu6502_1op("STA", "sprite_data+3"); + cpu6502_noop("PLA"); + } + } + generic_call("update_sprite"); } - generic_call("update_sprite"); } } else if (strcmp(name, "BITMAP") == 0) { generic_dump(); get_lex(); - if (lex != C_STRING || (name_size != 8 && name_size != 16)) { + if (lex != C_STRING ) { emit_error("syntax error in BITMAP"); - } else if (name_size == 16) { /* Sprites */ + } else if (name_size == 16) { /* 16x16 sprites */ int c; - value = 0; - for (c = 0; c < 16; c++) { - if (name[c] != 0x30 && name[c] != 0x5f /* 0 and _ */ - && name[c] != 0x20 && name[c] != 0x2e) /* space and . */ - value |= 0x8000 >> c; - } - get_lex(); - bitmap[bitmap_byte] = value >> 8; - bitmap[bitmap_byte + 16] = value; - bitmap_byte++; - if (bitmap_byte >= 16) { - bitmap_byte = 0; - for (c = 0; c < 32; c += 8) { - if (target == CPU_9900) { - sprintf(temp, "\tbyte >%02x,>%02x,>%02x,>%02x,>%02x,>%02x,>%02x,>%02x\n", - bitmap[c], bitmap[c + 1], bitmap[c + 2], bitmap[c + 3], - bitmap[c + 4], bitmap[c + 5], bitmap[c + 6], bitmap[c + 7]); - } else { + if (machine == NES) { + int first_time = 1; + + for (c = 0; c < 16; c++) { + if (name[c] != 0x30 && name[c] != 0x5f /* 0 and _ */ + && name[c] != 0x20 && name[c] != 0x2e) { /* space and . */ + int d; + + if (name[c] >= '0' && name[c] <= '3') { + d = name[c] - 0x30; + } else { + if (first_time) { + emit_warning("only 0-3 can be used in BITMAP"); + first_time = 0; + } + d = 0; + } + if (c < 8) { + value |= ((d & 1) << 7) >> c; + value |= ((d & 2) << 6) >> c << 8; + } else { + value |= ((d & 1) << 7) >> (c & 7) << 16; + value |= ((d & 2) << 6) >> (c & 7) << 24; + } + } + } + get_lex(); + if (current_chrrom == -1) { + emit_error("BITMAP used without choosing CHRROM table"); + } else if (chrrom_pointer + 48 >= CHRROM_PAGE) { + emit_error("More than 512 patterns"); + } else { + c = current_chrrom * CHRROM_PAGE + chrrom_pointer; + chrrom_data[c] = value; + chrrom_data[c + 8] = value >> 8; + chrrom_data[c + 32] = value >> 16; + chrrom_data[c + 40] = value >> 24; + chrrom_pointer++; + if ((chrrom_pointer & 7) == 0) + chrrom_pointer += 8; + if ((chrrom_pointer & 31) == 0) + chrrom_pointer += 32; + } + } else if (machine == SMS) { + int first_time = 1; + + value = 0; + for (c = 0; c < 8; c++) { + if (name[c] != 0x30 && name[c] != 0x5f /* 0 and _ */ + && name[c] != 0x20 && name[c] != 0x2e) { /* space and . */ + int d; + + if (isxdigit(name[c])) { + d = toupper(name[c]) - 0x30; + if (d > 9) + d -= 7; + } else { + if (first_time) { + emit_warning("invalid hexadecimal value in BITMAP"); + first_time = 0; + } + d = 0; + } + value |= ((d & 1) << 7) >> c; + value |= ((d & 2) << 6) >> c << 8; + value |= ((d & 4) << 5) >> c << 16; + value |= ((d & 8) << 4) >> c << 24; + } + } + bitmap[bitmap_byte + 0] = value; + bitmap[bitmap_byte + 1] = value >> 8; + bitmap[bitmap_byte + 2] = value >> 16; + bitmap[bitmap_byte + 3] = value >> 24; + value = 0; + for (c = 0; c < 8; c++) { + if (name[8 + c] != 0x30 && name[8 + c] != 0x5f /* 0 and _ */ + && name[8 + c] != 0x20 && name[8 + c] != 0x2e) { /* space and . */ + int d; + + if (isxdigit(name[8 + c])) { + d = toupper(name[8 + c]) - 0x30; + if (d > 9) + d -= 7; + } else { + if (first_time) { + emit_warning("invalid hexadecimal value in BITMAP"); + first_time = 0; + } + d = 0; + } + value |= ((d & 1) << 7) >> c; + value |= ((d & 2) << 6) >> c << 8; + value |= ((d & 4) << 5) >> c << 16; + value |= ((d & 8) << 4) >> c << 24; + } + } + get_lex(); + bitmap[bitmap_byte + 64] = value; + bitmap[bitmap_byte + 65] = value >> 8; + bitmap[bitmap_byte + 66] = value >> 16; + bitmap[bitmap_byte + 67] = value >> 24; + bitmap_byte += 4; + if (bitmap_byte == 64) { + bitmap_byte = 0; + for (c = 0; c < 128; c += 8) { sprintf(temp, "\tDB $%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x\n", bitmap[c], bitmap[c + 1], bitmap[c + 2], bitmap[c + 3], bitmap[c + 4], bitmap[c + 5], bitmap[c + 6], bitmap[c + 7]); + fprintf(output, "%s", temp); + } + } + } else { + value = 0; + for (c = 0; c < 16; c++) { + if (name[c] != 0x30 && name[c] != 0x5f /* 0 and _ */ + && name[c] != 0x20 && name[c] != 0x2e) /* space and . */ + value |= 0x8000 >> c; + } + get_lex(); + bitmap[bitmap_byte] = value >> 8; + bitmap[bitmap_byte + 16] = value; + bitmap_byte++; + if (bitmap_byte >= 16) { + bitmap_byte = 0; + for (c = 0; c < 32; c += 8) { + if (target == CPU_9900) { + sprintf(temp, "\tbyte >%02x,>%02x,>%02x,>%02x,>%02x,>%02x,>%02x,>%02x\n", + bitmap[c], bitmap[c + 1], bitmap[c + 2], bitmap[c + 3], + bitmap[c + 4], bitmap[c + 5], bitmap[c + 6], bitmap[c + 7]); + } else { + sprintf(temp, "\tDB $%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x\n", + bitmap[c], bitmap[c + 1], bitmap[c + 2], bitmap[c + 3], + bitmap[c + 4], bitmap[c + 5], bitmap[c + 6], bitmap[c + 7]); + } + fprintf(output, "%s", temp); } - fprintf(output, "%s", temp); } } - - } else { + } else if (name_size == 8) { int c; value = 0; - for (c = 0; c < 8; c++) { - if (name[c] != 0x30 && name[c] != 0x5f /* 0 and _ */ - && name[c] != 0x20 && name[c] != 0x2e) /* space and . */ - value |= 0x80 >> c; + if (machine == NES) { + int first_time = 1; + + for (c = 0; c < 8; c++) { + if (name[c] != 0x30 && name[c] != 0x5f /* 0 and _ */ + && name[c] != 0x20 && name[c] != 0x2e) { /* space and . */ + int d; + + if (name[c] >= '0' && name[c] <= '3') { + d = name[c] - 0x30; + } else { + if (first_time) { + emit_warning("only 0-3 can be used in BITMAP"); + first_time = 0; + } + d = 0; + } + value |= ((d & 1) << 7) >> c; + value |= ((d & 2) << 6) >> c << 8; + } + } + get_lex(); + if (current_chrrom == -1) { + emit_error("BITMAP used without choosing CHRROM table"); + } else if (chrrom_pointer == 8192) { + emit_error("More than 512 patterns"); + } else { + chrrom_data[current_chrrom * CHRROM_PAGE + chrrom_pointer] = value; + chrrom_data[current_chrrom * CHRROM_PAGE + chrrom_pointer + 8] = value >> 8; + chrrom_pointer++; + if ((chrrom_pointer & 7) == 0) + chrrom_pointer += 8; + } + } else if (machine == SMS) { + int first_time = 1; + + for (c = 0; c < 8; c++) { + if (name[c] != 0x30 && name[c] != 0x5f /* 0 and _ */ + && name[c] != 0x20 && name[c] != 0x2e) { /* space and . */ + int d; + + if (isxdigit(name[c])) { + d = toupper(name[c]) - 0x30; + if (d > 9) + d -= 7; + } else { + if (first_time) { + emit_warning("invalid hexadecimal value in BITMAP"); + first_time = 0; + } + d = 0; + } + value |= ((d & 1) << 7) >> c; + value |= ((d & 2) << 6) >> c << 8; + value |= ((d & 4) << 5) >> c << 16; + value |= ((d & 8) << 4) >> c << 24; + } + } + get_lex(); + bitmap[bitmap_byte++] = value; + bitmap[bitmap_byte++] = value >> 8; + bitmap[bitmap_byte++] = value >> 16; + bitmap[bitmap_byte++] = value >> 24; + } else { + for (c = 0; c < 8; c++) { + if (name[c] != 0x30 && name[c] != 0x5f /* 0 and _ */ + && name[c] != 0x20 && name[c] != 0x2e) /* space and . */ + value |= 0x80 >> c; + } + get_lex(); + bitmap[bitmap_byte] = value; + bitmap_byte++; } - get_lex(); - bitmap[bitmap_byte] = value; - bitmap_byte++; if (bitmap_byte >= 8) { bitmap_byte = 0; c = 0; @@ -3981,175 +4932,352 @@ void compile_statement(int check_for_else) } fprintf(output, "%s", temp); } + } else { + emit_error("width error in BITMAP"); } } else if (strcmp(name, "BORDER") == 0) { int type; get_lex(); - type = evaluate_expression(1, TYPE_8, 0); - if (target == CPU_6502) { - cpu6502_1op("LDX", "#7"); - cpu6502_noop("SEI"); - cpu6502_1op("JSR", "WRTVDP"); - cpu6502_noop("CLI"); - } else if (target == CPU_9900) { - /* this is just a lot faster inline than jumping through hoops... */ - cpu9900_2op("srl","r0","8"); - cpu9900_2op("ori","r0",">8700"); - cpu9900_1op("swpb","r0"); - cpu9900_1op("limi","0"); - cpu9900_2op("movb","r0","@VDPWADR"); - cpu9900_1op("swpb","r0"); - cpu9900_2op("movb","r0","@VDPWADR"); - cpu9900_1op("limi","2"); - } else { - cpuz80_2op("LD", "B", "A"); - cpuz80_2op("LD", "C", "7"); - cpuz80_1op("CALL", "nmi_off"); - cpuz80_1op("CALL", "WRTVDP"); - cpuz80_1op("CALL", "nmi_on"); + if (lex != C_COMMA) { + type = evaluate_expression(1, TYPE_8, 0); + if (target == CPU_6502) { + if (machine == NES) { + emit_warning("BORDER first argument ignored for NES/Famicom (use PALETTE 0)"); + } else { + cpu6502_1op("LDX", "#7"); + generic_interrupt_disable(); + cpu6502_1op("JSR", "WRTVDP"); + } + } else if (target == CPU_9900) { + /* this is just a lot faster inline than jumping through hoops... */ + cpu9900_2op("srl","r0","8"); + cpu9900_2op("ori","r0",">8700"); + cpu9900_1op("swpb","r0"); + generic_interrupt_disable(); + cpu9900_2op("movb","r0","@VDPWADR"); + cpu9900_1op("swpb","r0"); + cpu9900_2op("movb","r0","@VDPWADR"); + } else { + cpuz80_2op("LD", "B", "A"); + cpuz80_2op("LD", "C", "7"); + generic_interrupt_disable(); + cpuz80_1op("CALL", "WRTVDP"); + } + generic_interrupt_enable(); } - } else if (strcmp(name, "SIGNED") == 0) { - struct signedness *c; + if (lex == C_COMMA) { + get_lex(); + type = evaluate_expression(1, TYPE_8, 0); + if (machine != SMS && machine != NES && machine != MSX2) { + emit_error("The 2nd BORDER argument only available for MSX2/SMS/NES/Famicom"); + } else if (machine == SMS) { + cpuz80_1op("AND", "$07"); + cpuz80_1op("OR", "$20"); + cpuz80_noop("RRCA"); + cpuz80_noop("RRCA"); + cpuz80_noop("RRCA"); + cpuz80_2op("LD", "B", "A"); + cpuz80_2op("LD", "C", "0"); + generic_interrupt_disable(); + cpuz80_1op("CALL", "WRTVDP"); + generic_interrupt_enable(); + } else if (machine == MSX2) { + cpuz80_1op("AND", "$03"); + cpuz80_2op("LD", "B", "A"); + cpuz80_2op("LD", "C", "25"); + generic_interrupt_disable(); + cpuz80_1op("CALL", "WRTVDP"); + generic_interrupt_enable(); + } else if (machine == NES) { + cpu6502_1op("ASL", "A"); + cpu6502_1op("AND", "#$06"); + cpu6502_noop("PHA"); + cpu6502_1op("LDA", "ppu_mask"); + cpu6502_1op("AND", "#$f9"); + cpu6502_1op("STA", "ppu_mask"); + cpu6502_noop("PLA"); + cpu6502_1op("ORA", "ppu_mask"); + cpu6502_1op("STA", "ppu_mask"); + } + } + } else if (strcmp(name, "SCROLL") == 0) { /* Sega Master System / MSX2 / NES/Famicom */ + int type; get_lex(); - while (1) { - if (lex != C_NAME) { - emit_error("missing name in SIGNED"); - break; - } - c = signed_search(name); - if (c != NULL) { - emit_error("variable already SIGNED/UNSIGNED"); + if (machine != SMS && machine != NES && machine != MSX2) + emit_error("SCROLL is only available for MSX2/SMS/NES/Famicom"); + if (lex != C_COMMA) { + if (machine == SMS) { + type = evaluate_expression(1, TYPE_8, 0); + cpuz80_2op("LD", "B", "A"); + cpuz80_2op("LD", "C", "8"); + generic_interrupt_disable(); + cpuz80_1op("CALL", "WRTVDP"); + generic_interrupt_enable(); + } else if (machine == MSX2) { + type = evaluate_expression(1, TYPE_16, 0); + cpuz80_2op("LD", "A", "L"); + cpuz80_1op("AND", "7"); + cpuz80_2op("LD", "B", "A"); + cpuz80_2op("LD", "C", "27"); + generic_interrupt_disable(); + cpuz80_1op("CALL", "WRTVDP"); + cpuz80_2op("LD", "A", "L"); + cpuz80_1op("SRL", "H"); + cpuz80_noop("RRA"); + cpuz80_noop("RRA"); + cpuz80_noop("RRA"); + cpuz80_1op("AND", "63"); + cpuz80_2op("LD", "B", "A"); + cpuz80_2op("LD", "C", "26"); + cpuz80_1op("CALL", "WRTVDP"); + generic_interrupt_enable(); } else { - c = signed_add(name); + type = evaluate_expression(1, TYPE_16, 0); + cpu6502_1op("STA", "scroll_x"); + cpu6502_1op("STY", "scroll_x+1"); } - c->sign = 1; - get_lex(); - if (lex != C_COMMA) - break; + } + if (lex == C_COMMA) { get_lex(); + if (machine == SMS) { + type = evaluate_expression(1, TYPE_8, 0); + cpuz80_2op("LD", "B", "A"); + cpuz80_2op("LD", "C", "9"); + generic_interrupt_disable(); + cpuz80_1op("CALL", "WRTVDP"); + generic_interrupt_enable(); + } else if (machine == MSX2) { + type = evaluate_expression(1, TYPE_8, 0); + cpuz80_2op("LD", "B", "A"); + cpuz80_2op("LD", "C", "23"); + generic_interrupt_disable(); + cpuz80_1op("CALL", "WRTVDP"); + generic_interrupt_enable(); + } else { + type = evaluate_expression(1, TYPE_16, 0); + cpu6502_1op("STA", "scroll_y"); + cpu6502_1op("STY", "scroll_y+1"); + } } - } else if (strcmp(name, "UNSIGNED") == 0) { - struct signedness *c; - + } else if (strcmp(name, "PALETTE") == 0) { /* Sega Master System / NES / MSX2 */ + int type; + struct node *source; + get_lex(); - while (1) { + if (machine != SMS && machine != NES && machine != MSX && machine != MSX2) + emit_error("PALETTE is only available on SMS/NES/Famicom/MSX/MSX2"); + if (lex == C_NAME && strcmp(name, "LOAD") == 0) { + get_lex(); if (lex != C_NAME) { - emit_error("missing name in UNSIGNED"); - break; - } - c = signed_search(name); - if (c != NULL) { - emit_error("variable already SIGNED/UNSIGNED"); + emit_error("missing label in PALETTE LOAD"); + } else if (strcmp(name, "VARPTR") == 0) { + source = evaluate_save_expression(1, TYPE_16); /* CPU address (variable) */ + node_generate(source, 0); + node_delete(source); } else { - c = signed_add(name); + if (machine == SMS || machine == MSX2 || machine == MSX) { + strcpy(temp, LABEL_PREFIX); + strcat(temp, name); + cpuz80_2op("LD", "HL", temp); + generic_call("palette_load"); + } else if (machine == NES) { + cpu6502_1op("LDA", "#0"); + cpu6502_1op("STA", "pointer"); + cpu6502_1op("LDA", "#63"); + cpu6502_1op("STA", "pointer+1"); + cpu6502_1op("LDA", "#32"); + cpu6502_1op("STA", "temp2"); + strcpy(temp, "#" LABEL_PREFIX); + strcat(temp, name); + cpu6502_1op("LDA", temp); + cpu6502_1op("STA", "temp"); + strcat(temp, ">>8"); + cpu6502_1op("LDA", temp); + cpu6502_1op("STA", "temp+1"); + cpu6502_1op("JSR", "LDIRVM"); + } + get_lex(); } - c->sign = 2; + } else if (lex == C_NAME && strcmp(name, "DEFAULT") == 0) { get_lex(); + if (machine != MSX2 && machine != SMS && machine != MSX) + emit_error("PALETTE DEFAULT is only available in MSX2/SMS"); + generic_call("palette_default"); + } else { + type = evaluate_expression(1, TYPE_8, 0); + if (machine == SMS) { + generic_interrupt_disable(); + cpuz80_2op("LD", "L", "A"); + cpuz80_2op("LD", "H", "$C0"); + cpuz80_1op("CALL", "SETWRT"); + } else if (machine == NES) { + cpu6502_1op("LDX", "ppu_pointer"); + cpu6502_1op("STA", "PPUBUF,X"); + cpu6502_1op("LDA", "#$7f"); + cpu6502_1op("STA", "PPUBUF+1,X"); + } else if (machine == MSX2 || machine == MSX) { + generic_interrupt_disable(); + cpuz80_2op("LD", "B", "A"); + cpuz80_2op("LD", "C", "$10"); + if (machine == MSX) { + cpuz80_2op("LD", "A", "($002d)"); + cpuz80_1op("OR", "A"); /* Detect MSX1 and jump to avoid */ + cpuz80_2op("JR", "Z", "$+5"); + } + cpuz80_1op("CALL", "WRTVDP"); + } if (lex != C_COMMA) - break; - get_lex(); + emit_error("missing comma in PALETTE"); + else + get_lex(); + type = evaluate_expression(1, ((machine == MSX2 || machine == MSX) ? TYPE_16 : TYPE_8), 0); + if (machine == SMS) { + cpuz80_2op("OUT", "(VDP)", "A"); + generic_interrupt_enable(); + } else if (machine == NES) { + cpu6502_1op("LDX", "ppu_pointer"); + cpu6502_1op("STA", "PPUBUF+2,X"); + cpu6502_noop("INX"); + cpu6502_noop("INX"); + cpu6502_noop("INX"); + cpu6502_1op("STX", "ppu_pointer"); + } else if (machine == MSX2 || machine == MSX) { + if (machine == MSX) { + cpuz80_2op("LD", "A", "($002d)"); + cpuz80_1op("OR", "A"); /* Detect MSX1 and jump to avoid */ + cpuz80_2op("JR", "Z", "$+8"); + } + cpuz80_2op("LD", "A", "L"); + cpuz80_2op("OUT", "(VDP+2)", "A"); + cpuz80_2op("LD", "A", "H"); + cpuz80_2op("OUT", "(VDP+2)", "A"); + generic_interrupt_enable(); + } } - } else if (strcmp(name, "CONST") == 0) { - struct constant *c; + } else if (strcmp(name, "NAMETABLE") == 0) { /* Select nametable arrangement */ + int type; + struct node *tree; + int c; + if (machine != NES) + emit_error("CHRRAM is only allowed for NES/Famicom"); + else if (bank_switching == 0) + emit_error("It is required a BANK ROM sentence to enable CHRRAM"); get_lex(); - if (lex != C_NAME) { - emit_error("name required for constant assignment"); - return; - } - c = constant_search(name); - if (c != NULL) { - emit_error("constant redefined"); + tree = evaluate_level_0(&type); + if (tree->type != N_NUM8 && tree->type != N_NUM16) { + emit_error("Not a constant expression in CHRRAM"); + c = 0; } else { - c = constant_add(name); + c = tree->value; } - strcpy(assigned, name);; + node_delete(tree); + if (c < 0 || c > 3) + emit_error("NAMETABLE value outside of range 0-3"); + nes_nametable = c; + } else if (strcmp(name, "CHRRAM") == 0) { /* Select CHRRAM */ + int type; + struct node *tree; + int c; + + if (machine != NES) + emit_error("CHRRAM is only allowed for NES/Famicom"); + else if (bank_switching == 0) + emit_error("It is required a BANK ROM sentence to enable CHRRAM"); get_lex(); - if (lex != C_EQUAL) { - emit_error("required '=' for constant assignment"); + tree = evaluate_level_0(&type); + if (tree->type != N_NUM8 && tree->type != N_NUM16) { + emit_error("Not a constant expression in CHRRAM"); + c = 0; } else { - struct node *tree; - int type; - - get_lex(); - tree = evaluate_level_0(&type); - if (tree->type != N_NUM8 && tree->type != N_NUM16) { - emit_error("not a constant expression in CONST"); - } else { - c->value = tree->value; - } - if (target == CPU_Z80 || target == CPU_6502) { - sprintf(temp, CONST_PREFIX "%s:\tequ $%04x", assigned, c->value); - fprintf(output, "%s\n", temp); - } else if (target == CPU_9900) { - sprintf(temp, CONST_PREFIX "%s\tequ >%04x", assigned, c->value); - cpu9900_label(temp); // Hack - } - node_delete(tree); - tree = NULL; + c = tree->value; } - } else if (strcmp(name, "DIM") == 0) { - char array[MAX_LINE_SIZE]; - struct label *new_array; - struct node *tree; + node_delete(tree); + if (c < 0 || c > 7) + emit_error("CHRRAM value outside of range 0-7"); + c = c << 5; + sprintf(temp, "#%d", c); + cpu6502_1op("LDA", temp); + cpu6502_1op("STA", "CHRRAM_BANK"); + } else if (strcmp(name, "CHRROM") == 0) { /* Select CHRROM */ int type; + struct node *tree; int c; - while (1) { - get_lex(); - if (lex != C_NAME) { - emit_error("missing name in DIM"); - break; + if (machine != NES) + emit_error("CHRROM is only allowed for NES/Famicom"); + get_lex(); + if (lex != C_NAME || strcmp(name, "PATTERN") != 0) { + tree = evaluate_level_0(&type); + if (tree->type != N_NUM8 && tree->type != N_NUM16) { + emit_error("Not a constant expression in CHRROM"); + c = 0; + } else { + c = tree->value; } - strcpy(array, name); - get_lex(); - if (lex == C_LPAREN) { - get_lex(); - tree = evaluate_level_0(&type); - if (tree->type != N_NUM8 && tree->type != N_NUM16) { - emit_error("not a constant expression in DIM"); + node_delete(tree); + if (c + 1 > chrrom_size) { /* Grow the CHRROM */ + unsigned char *p; + + p = realloc(chrrom_data, (c + 1) * CHRROM_PAGE); + if (p == NULL) { + emit_error("Not enough memory for CHRROM"); break; } - c = tree->value; - node_delete(tree); - if (lex != C_RPAREN) { - emit_error("missing right parenthesis in DIM"); - } else { - get_lex(); - } - new_array = array_search(array); - if (new_array != NULL) { - emit_error("array already defined"); - } else { - new_array = array_add(array); - new_array->length = c; - } + chrrom_data = p; + /* Reset pixel data in new space */ + for (type = chrrom_size; type < c + 1; type++) + reset_chrrom(chrrom_data + chrrom_size * CHRROM_PAGE); + chrrom_size = c + 1; + } + current_chrrom = c; + chrrom_pointer = 0; + } + if (lex == C_NAME && strcmp(name, "PATTERN") == 0) { + get_lex(); + tree = evaluate_level_0(&type); + if (tree->type != N_NUM8 && tree->type != N_NUM16) { + emit_error("Not a constant expression in CHRROM"); + c = 0; } else { - label = label_add(array); - if (array[0] == '#') - label->used = TYPE_16; - else - label->used = TYPE_8; - label->used |= LABEL_IS_VARIABLE; + c = tree->value; } - if (lex != C_COMMA) - break; + node_delete(tree); + if (c < 0 || c > 511) { + emit_error("CHRROM PATTERN out of range 0-511"); + c = 0; + } + chrrom_pointer = c * 16; } } else if (strcmp(name, "MODE") == 0) { get_lex(); - if (lex != C_NUM || (value != 0 && value != 1 && value != 2)) { + if (lex != C_NUM || (value != 0 && value != 1 && value != 2 && value != 4)) { emit_error("bad syntax for MODE"); break; } get_lex(); - if (value == 0) + if (value == 0) { + if (machine == SMS) + emit_warning("MODE 0 isn't upward compatible with Sega Genesis/Mega Drive"); generic_call("mode_0"); - if (value == 1) + } + if (value == 1) { + if (machine == SMS) + emit_warning("MODE 1 isn't upward compatible with Sega Genesis/Mega Drive"); generic_call("mode_1"); - if (value == 2) + } + if (value == 2) { + if (machine == SMS) + emit_warning("MODE 2 isn't upward compatible with Sega Genesis/Mega Drive"); generic_call("mode_2"); + } + if (value == 4) { + if (machine != SMS && machine != MSX2) + emit_error("MODE 4 not supported by this platform"); + generic_call("mode_4"); + } } else if (strcmp(name, "SCREEN") == 0) { /* Copy screen */ struct label *array; @@ -4183,13 +5311,16 @@ void compile_statement(int check_for_else) struct node *final; struct node *addr; int type; + int c; get_lex(); addr = node_create(N_ADDR, 0, NULL, NULL); addr->label = array; - final = evaluate_level_0(&type); /* Source */ + final = evaluate_level_0(&type); /* Source offset */ if ((type & MAIN_TYPE) == TYPE_8) final = node_create((type & TYPE_SIGNED) ? N_EXTEND8S : N_EXTEND8, 0, final, NULL); + if (machine == SMS) + final = node_create(N_MUL16, 0, final, node_create(N_NUM16, 2, NULL, NULL)); final = node_create(N_PLUS16, 0, addr, final); node_label(final); node_generate(final, 0); @@ -4208,10 +5339,18 @@ void compile_statement(int check_for_else) break; } get_lex(); - final = evaluate_level_0(&type); /* Target */ + final = evaluate_level_0(&type); /* Target offset */ if ((type & MAIN_TYPE) == TYPE_8) final = node_create((type & TYPE_SIGNED) ? N_EXTEND8S : N_EXTEND8, 0, final, NULL); - final = node_create(N_PLUS16, 0, node_create(N_NUM16, 0x1800, NULL, NULL), final); + if (machine == SMS) + final = node_create(N_MUL16, 0, final, node_create(N_NUM16, 2, NULL, NULL)); + if (machine == SMS) + c = 0x3800; + else if (machine == NES) + c = 0x2000; + else + c = 0x1800; + final = node_create(N_PLUS16, 0, node_create(N_NUM16, c, NULL, NULL), final); node_label(final); node_generate(final, 0); node_delete(final); @@ -4228,9 +5367,11 @@ void compile_statement(int check_for_else) break; } get_lex(); - final = evaluate_level_0(&type); /* Width */ + final = evaluate_level_0(&type); /* Source/target Width */ if ((type & MAIN_TYPE) == TYPE_16) final = node_create(N_REDUCE16, 0, final, NULL); + if (machine == SMS) + final = node_create(N_MUL8, 0, final, node_create(N_NUM8, 2, NULL, NULL)); node_label(final); node_generate(final, 0); node_delete(final); @@ -4246,7 +5387,7 @@ void compile_statement(int check_for_else) break; } get_lex(); - final = evaluate_level_0(&type); /* Height */ + final = evaluate_level_0(&type); /* Source/target height */ if ((type & MAIN_TYPE) == TYPE_16) final = node_create(N_REDUCE16, 0, final, NULL); node_label(final); @@ -4268,6 +5409,8 @@ void compile_statement(int check_for_else) } else { if ((type & MAIN_TYPE) == TYPE_16) final = node_create(N_REDUCE16, 0, final, NULL); + if (machine == SMS) + final = node_create(N_MUL8, 0, final, node_create(N_NUM8, 2, NULL, NULL)); } node_label(final); node_generate(final, 0); @@ -4303,476 +5446,338 @@ void compile_statement(int check_for_else) cpu6502_1op("STA","temp+1"); cpu6502_noop("PLA"); cpu6502_1op("STA","temp"); - } /* 9900 already covered */ - generic_call("CPYBLK"); - } else { - if (target == CPU_6502) { - sprintf(temp, "#%s", assigned); - cpu6502_1op("LDA", temp); - strcat(temp, ">>8"); - cpu6502_1op("LDY", temp); - cpu6502_1op("STA", "temp"); - cpu6502_1op("STY", "temp+1"); - cpu6502_1op("LDA", "#0"); - cpu6502_1op("LDY", "#3"); - cpu6502_1op("STA", "temp2"); - cpu6502_1op("STY", "temp2+1"); - cpu6502_1op("LDA", "#0"); - cpu6502_1op("LDY", "#24"); - cpu6502_1op("STA", "pointer"); - cpu6502_1op("STY", "pointer+1"); - cpu6502_noop("SEI"); - cpu6502_1op("JSR", "LDIRVM"); - cpu6502_noop("CLI"); - } else if (target == CPU_9900) { - cpu9900_2op("li","r0",">1800"); - cpu9900_2op("li","r2",assigned); - cpu9900_2op("li","r3",">0300"); - cpu9900_1op("limi","0"); - cpu9900_1op("bl","@jsr"); - cpu9900_1op("data","LDIRVM"); - cpu9900_1op("limi","2"); - } else { - cpuz80_2op("LD", "HL", assigned); - cpuz80_2op("LD", "DE", "$1800"); - cpuz80_2op("LD", "BC", "$0300"); - cpuz80_1op("CALL", "nmi_off"); - cpuz80_1op("CALL", "LDIRVM"); - cpuz80_1op("CALL", "nmi_on"); - } - } - } - } else if (strcmp(name, "PLAY") == 0) { - int c; - - get_lex(); - if (lex != C_NAME) { - emit_error("bad syntax for PLAY"); - break; - } - music_used = 1; - if (strcmp(name, "OFF") == 0) { - get_lex(); - if (target == CPU_6502) { - cpu6502_1op("LDA", "#music_silence"); - cpu6502_1op("LDY", "#music_silence>>8"); - } else if (target == CPU_9900) { - cpu9900_2op("li", "r0", "music_silence"); - } else { - cpuz80_2op("LD", "HL", "music_silence"); - } - generic_call("music_play"); - } else if (strcmp(name, "NONE") == 0) { - get_lex(); - if (target == CPU_6502) { - cpu6502_1op("LDA", "#0"); - cpu6502_1op("STA", "music_mode"); - } else if (target == CPU_9900) { - cpu9900_1op("clr", "r0"); - cpu9900_2op("movb", "r0", "@music_mode"); - } else { - cpuz80_1op("XOR", "A"); - cpuz80_2op("LD", "(music_mode)", "A"); - } - } else if (strcmp(name, "SIMPLE") == 0) { - get_lex(); - c = 3; - if (lex == C_NAME && strcmp(name, "NO") == 0) { - get_lex(); - if (lex == C_NAME && strcmp(name, "DRUMS") == 0) { - get_lex(); - c = 2; - } else { - emit_error("only allowed PLAY SIMPLE NO DRUMS"); - } - } - if (target == CPU_6502) { - sprintf(temp, "#%d", c); - cpu6502_1op("LDA", temp); - cpu6502_1op("STA", "music_mode"); - } else if (target == CPU_9900) { - sprintf(temp, "%d ; %d*256", c*256, c); - cpu9900_2op("li", "r0", temp); - cpu9900_2op("movb", "r0", "@music_mode"); - } else { - sprintf(temp, "%d", c); - cpuz80_2op("LD", "A", temp); - cpuz80_2op("LD", "(music_mode)", "A"); - } - } else if (strcmp(name, "FULL") == 0) { - get_lex(); - c = 5; - if (lex == C_NAME && strcmp(name, "NO") == 0) { - get_lex(); - if (lex == C_NAME && strcmp(name, "DRUMS") == 0) { - get_lex(); - c = 4; - } else { - emit_error("only allowed PLAY FULL NO DRUMS"); - } - } - if (target == CPU_6502) { - sprintf(temp, "#%d", c); - cpu6502_1op("LDA", temp); - cpu6502_1op("STA", "music_mode"); - } else if (target == CPU_9900) { - sprintf(temp, "%d ; %d*256", c*256, c); - cpu9900_2op("li", "r0", temp); - cpu9900_2op("movb", "r0", "@music_mode"); - } else { - sprintf(temp, "%d", c); - cpuz80_2op("LD", "A", temp); - cpuz80_2op("LD", "(music_mode)", "A"); - } - } else { - struct label *label; - - label = label_search(name); - if (label == NULL) { - label = label_add(name); - } - label->used |= LABEL_USED; - if (target == CPU_6502) { - strcpy(temp, "#" LABEL_PREFIX); - strcat(temp, name); - cpu6502_1op("LDA", temp); - strcat(temp, ">>8"); - cpu6502_1op("LDY", temp); - } else if (target == CPU_9900) { - strcpy(temp, LABEL_PREFIX); - strcat(temp, name); - cpu9900_2op("li", "r0", temp); - } else { - strcpy(temp, LABEL_PREFIX); - strcat(temp, name); - cpuz80_2op("LD", "HL", temp); - } - generic_call("music_play"); - get_lex(); - } - } else if (strcmp(name, "MUSIC") == 0) { - int arg; - static int previous[4]; - unsigned int notes; - int note; - int c; - int label; - - generic_dump(); - get_lex(); - label = 0; - notes = 0; - arg = 0; - while (1) { - if (lex != C_NAME && lex != C_MINUS) { - emit_error("bad syntax for MUSIC"); - break; - } - if (lex == C_MINUS) { - /* Nothing to do */ - } else if (arg == 0 && strcmp(name, "REPEAT") == 0) { - get_lex(); - notes = 0xfd; - break; - } else if (arg == 0 && strcmp(name, "STOP") == 0) { - get_lex(); - notes = 0xfe; - break; - } else if (arg == 3) { - if (name[0] != 'M' || name[1] < '1' || name[1] > '3') { - emit_error("bad syntax for drum in MUSIC"); - break; - } - notes |= (name[1] - '0') << ((arg & 3) * 8); - } else if (strcmp(name, "S") == 0) { - notes |= 0x3f << ((arg & 3) * 8); - } else { - notes |= previous[arg] << ((arg & 3) * 8); - c = 0; - switch (name[c++]) { - case 'C': note = 0; break; - case 'D': note = 2; break; - case 'E': note = 4; break; - case 'F': note = 5; break; - case 'G': note = 7; break; - case 'A': note = 9; break; - case 'B': note = 11; break; - default: - note = 0; - emit_error("bad syntax for note in MUSIC"); - break; - } - switch (name[c++]) { - case '2': note += 0 * 12; break; - case '3': note += 1 * 12; break; - case '4': note += 2 * 12; break; - case '5': note += 3 * 12; break; - case '6': note += 4 * 12; break; - case '7': if (note == 0) { note += 5 * 12; break; } - default: - emit_error("bad syntax for note in MUSIC"); - break; - } - note++; - if (name[c] == '#') { - note++; - c++; - } - if (name[c] == 'W') { - previous[arg] = 0x00; - notes &= ~(0xc0 << ((arg & 3) * 8)); - notes |= previous[arg] << ((arg & 3) * 8); - } else if (name[c] == 'X') { - previous[arg] = 0x40; - notes &= ~(0xc0 << ((arg & 3) * 8)); - notes |= previous[arg] << ((arg & 3) * 8); - } else if (name[c] == 'Y') { - previous[arg] = 0x80; - notes &= ~(0xc0 << ((arg & 3) * 8)); - notes |= previous[arg] << ((arg & 3) * 8); - } else if (name[c] == 'Z') { - previous[arg] = 0xc0; - notes &= ~(0xc0 << ((arg & 3) * 8)); - notes |= previous[arg] << ((arg & 3) * 8); + } /* 9900 already covered */ + generic_call("CPYBLK"); + } else { + if (target == CPU_6502) { + if (machine == NES) { + sprintf(temp, "#%s", assigned); + cpu6502_1op("LDA", temp); + strcat(temp, ">>8"); + cpu6502_1op("LDY", temp); + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); + cpu6502_1op("LDA", "#$f0"); + cpu6502_1op("LDY", "#0"); + cpu6502_1op("STA", "temp2"); + cpu6502_1op("STY", "temp2+1"); + cpu6502_1op("LDA", "#$00"); + cpu6502_1op("LDY", "#$20"); + cpu6502_1op("STA", "pointer"); + cpu6502_1op("STY", "pointer+1"); + cpu6502_1op("JSR", "LDIRVM4"); + } else { + sprintf(temp, "#%s", assigned); + cpu6502_1op("LDA", temp); + strcat(temp, ">>8"); + cpu6502_1op("LDY", temp); + cpu6502_1op("STA", "temp"); + cpu6502_1op("STY", "temp+1"); + cpu6502_1op("LDA", "#0"); + cpu6502_1op("LDY", "#3"); + cpu6502_1op("STA", "temp2"); + cpu6502_1op("STY", "temp2+1"); + cpu6502_1op("LDA", "#$00"); + cpu6502_1op("LDY", "#$18"); + cpu6502_1op("STA", "pointer"); + cpu6502_1op("STY", "pointer+1"); + generic_interrupt_disable(); + cpu6502_1op("JSR", "LDIRVM"); + generic_interrupt_enable(); + } + } else if (target == CPU_9900) { + cpu9900_2op("li","r0",">1800"); + cpu9900_2op("li","r2",assigned); + cpu9900_2op("li","r3",">0300"); + generic_interrupt_disable(); + cpu9900_1op("bl","@jsr"); + cpu9900_1op("data","LDIRVM"); + generic_interrupt_enable(); + } else { + cpuz80_2op("LD", "HL", assigned); + cpuz80_2op("LD", "DE", (machine == SMS ? "$3800" : "$1800")); + cpuz80_2op("LD", "BC", (machine == SMS ? "$0600" : "$0300")); + generic_interrupt_disable(); + cpuz80_1op("CALL", "LDIRVM"); + generic_interrupt_enable(); } - notes |= note << ((arg & 3) * 8); - } - get_lex(); - arg++; - if (lex != C_COMMA) - break; - if (arg == 4) { - emit_error("too many arguments for MUSIC"); - break; } - get_lex(); - } - if (target == CPU_9900) { - fprintf(output, "\tbyte >%02x,>%02x,>%02x,>%02x\n", notes & 0xff, (notes >> 8) & 0xff, (notes >> 16) & 0xff, (notes >> 24) & 0xff); - } else { - fprintf(output, "\tdb $%02x,$%02x,$%02x,$%02x\n", notes & 0xff, (notes >> 8) & 0xff, (notes >> 16) & 0xff, (notes >> 24) & 0xff); } - } else if (strcmp(name, "ON") == 0) { - struct label *label; - int table; + } else if (strcmp(name, "PLAY") == 0) { int c; - int max_value; - int gosub; - int fast; - struct label *options[256]; - int new_label; - int type; get_lex(); - if (lex == C_NAME && strcmp(name, "FRAME") == 0) { /* Frame-driven games */ + if (lex != C_NAME) { + emit_error("bad syntax for PLAY"); + break; + } + music_used = 1; + if (strcmp(name, "OFF") == 0) { get_lex(); - if (lex != C_NAME || strcmp(name, "GOSUB") != 0) { - emit_error("Bad syntax for ON FRAME GOSUB"); + if (target == CPU_6502) { + cpu6502_1op("LDA", "#music_silence"); + cpu6502_1op("LDY", "#music_silence>>8"); + } else if (target == CPU_9900) { + cpu9900_2op("li", "r0", "music_silence"); + } else { + cpuz80_2op("LD", "HL", "music_silence"); } + generic_call("music_play"); + } else if (strcmp(name, "NONE") == 0) { get_lex(); - if (lex != C_NAME) { - emit_error("Missing label for ON FRAME GOSUB"); + if (target == CPU_6502) { + cpu6502_1op("LDA", "#0"); + cpu6502_1op("STA", "music_mode"); + } else if (target == CPU_9900) { + cpu9900_1op("clr", "r0"); + cpu9900_2op("movb", "r0", "@music_mode"); + } else { + cpuz80_1op("XOR", "A"); + cpuz80_2op("LD", "(music_mode)", "A"); } - if (frame_drive != NULL) { - emit_error("More than one ON FRAME GOSUB"); + } else if (strcmp(name, "SIMPLE") == 0) { + get_lex(); + c = 3; + if (lex == C_NAME && strcmp(name, "NO") == 0) { + get_lex(); + if (lex == C_NAME && strcmp(name, "DRUMS") == 0) { + get_lex(); + c = 2; + } else { + emit_error("only allowed PLAY SIMPLE NO DRUMS"); + } } - label = label_search(name); - if (label == NULL) { - label = label_add(name); + if (target == CPU_6502) { + sprintf(temp, "#%d", c); + cpu6502_1op("LDA", temp); + cpu6502_1op("STA", "music_mode"); + } else if (target == CPU_9900) { + sprintf(temp, "%d ; %d*256", c*256, c); + cpu9900_2op("li", "r0", temp); + cpu9900_2op("movb", "r0", "@music_mode"); + } else { + sprintf(temp, "%d", c); + cpuz80_2op("LD", "A", temp); + cpuz80_2op("LD", "(music_mode)", "A"); } - label->used |= LABEL_USED; - label->used |= LABEL_CALLED_BY_GOSUB; - frame_drive = label; + } else if (strcmp(name, "FULL") == 0) { get_lex(); - } else { - type = evaluate_expression(0, 0, 0); - fast = 0; - if (lex == C_NAME && strcmp(name, "FAST") == 0) { + c = 5; + if (lex == C_NAME && strcmp(name, "NO") == 0) { get_lex(); - fast = 1; + if (lex == C_NAME && strcmp(name, "DRUMS") == 0) { + get_lex(); + c = 4; + } else { + emit_error("only allowed PLAY FULL NO DRUMS"); + } } - gosub = 0; - if (lex != C_NAME || (strcmp(name, "GOTO") != 0 && strcmp(name, "GOSUB") != 0)) { - emit_error("required GOTO or GOSUB after ON"); - } else if (strcmp(name, "GOTO") == 0) { + if (target == CPU_6502) { + sprintf(temp, "#%d", c); + cpu6502_1op("LDA", temp); + cpu6502_1op("STA", "music_mode"); + } else if (target == CPU_9900) { + sprintf(temp, "%d ; %d*256", c*256, c); + cpu9900_2op("li", "r0", temp); + cpu9900_2op("movb", "r0", "@music_mode"); + } else { + sprintf(temp, "%d", c); + cpuz80_2op("LD", "A", temp); + cpuz80_2op("LD", "(music_mode)", "A"); + } + } else if (strcmp(name, "FM") == 0) { + get_lex(); + if (machine != MSX && machine != MSX2 && machine != SMS) + emit_warning("PLAY FM only allowed for MSX/MSX2/SMS"); + if (lex == C_NAME && strcmp(name, "ON") == 0) { get_lex(); - } else if (strcmp(name, "GOSUB") == 0) { + cpuz80_2op("LD", "A", "1"); + cpuz80_2op("LD", "(fm_enabled)", "A"); + } else if (lex == C_NAME && strcmp(name, "OFF") == 0) { get_lex(); - gosub = 1; + cpuz80_1op("CALL", "turn_off_fm"); + cpuz80_1op("XOR", "A"); + cpuz80_2op("LD", "(fm_enabled)", "A"); + } else { + emit_error("syntax error in PLAY FM"); } - max_value = 0; - while (1) { - if (max_value == sizeof(options) / sizeof(struct label *)) { - emit_error("too many options for ON statement"); - max_value--; - } - if (lex == C_NAME) { - label = label_search(name); - if (label == NULL) { - label = label_add(name); - } - label->used |= LABEL_USED; - if (gosub != 0) { - label->used |= LABEL_CALLED_BY_GOSUB; - } else { - label->used |= LABEL_CALLED_BY_GOTO; - } - options[max_value++] = label; + } else if (strcmp(name, "INSTRUMENT") == 0) { + get_lex(); + if (machine != MSX && machine != MSX2 && machine != SMS) + emit_warning("PLAY INSTRUMENT only allowed for MSX/MSX2/SMS"); + if (lex == C_NAME && strcmp(name, "VARPTR") == 0) { + struct node *source = NULL; + + source = evaluate_save_expression(1, TYPE_16); /* CPU address (variable) */ + node_generate(source, 0); + cpuz80_1op("CALL", "program_fm"); + node_delete(source); + } else { + type = evaluate_expression(1, TYPE_8, 0); + if (target == CPU_Z80) + cpuz80_2op("LD", "(fm_inst)", "A"); + if (lex != C_COMMA) + emit_error("missing comma"); + else get_lex(); - } else { - options[max_value++] = NULL; - } + type = evaluate_expression(1, TYPE_8, 0); + if (target == CPU_Z80) + cpuz80_2op("LD", "(fm_inst+1)", "A"); if (lex != C_COMMA) - break; - get_lex(); - } - table = next_local++; - new_label = next_local++; - if (fast == 0) { - if (target == CPU_6502) { - if ((type & MAIN_TYPE) == TYPE_8) { - cpu6502_1op("STA", "temp"); - cpu6502_1op("LDY", "#0"); - cpu6502_1op("STY", "temp+1"); - sprintf(temp, "#%d", max_value); - cpu6502_1op("CMP", temp); - } else { - cpu6502_1op("STA", "temp"); - cpu6502_1op("STY", "temp+1"); - sprintf(temp, "#%d", max_value); - cpu6502_1op("LDA", "temp"); - cpu6502_noop("SEC"); - cpu6502_1op("SBC", temp); - cpu6502_1op("LDA", "temp+1"); - strcat(temp, ">>8"); - cpu6502_1op("SBC", temp); - } - sprintf(temp, INTERNAL_PREFIX "%d", new_label); - cpu6502_1op("BCS.L", temp); - } else if (target == CPU_9900) { - if ((type & MAIN_TYPE) == TYPE_8) { - sprintf(temp, "%d ; %d*256", max_value*256, max_value); - cpu9900_2op("ci", "r0", temp); - } else { - sprintf(temp, "%d", max_value); - cpu9900_2op("ci", "r0", temp); - } - sprintf(temp, "@" INTERNAL_PREFIX "%d", new_label); - sprintf(temp + 100, INTERNAL_PREFIX "%d", next_local++); - cpu9900_1op("jl", temp + 100); - cpu9900_1op("b", temp); - cpu9900_label(temp + 100); - } else { - if ((type & MAIN_TYPE) == TYPE_8) { - sprintf(temp, "%d", max_value); - cpuz80_1op("CP", temp); - } else { - sprintf(temp, "%d", max_value); - cpuz80_2op("LD", "DE", temp); - cpuz80_1op("OR", "A"); - cpuz80_2op("SBC", "HL", "DE"); - cpuz80_2op("ADD", "HL", "DE"); - } - sprintf(temp, INTERNAL_PREFIX "%d", new_label); - cpuz80_2op("JP", "NC", temp); - } - } - if (gosub) { - if (target == CPU_6502) { - sprintf(temp, "#(" INTERNAL_PREFIX "%d-1)>>8", new_label); - cpu6502_1op("LDA", temp); - cpu6502_noop("PHA"); - sprintf(temp, "#" INTERNAL_PREFIX "%d-1", new_label); - cpu6502_1op("LDA", temp); - cpu6502_noop("PHA"); - } else if (target == CPU_9900) { - sprintf(temp, INTERNAL_PREFIX "%d", new_label); - cpu9900_2op("li", "r1", temp); - cpu9900_1op("dect", "r10"); /* stack manipulation */ - cpu9900_2op("mov", "r1", "*r10"); - } else { - sprintf(temp, INTERNAL_PREFIX "%d", new_label); - cpuz80_2op("LD", "DE", temp); - cpuz80_1op("PUSH", "DE"); - } + emit_error("missing comma"); + else + get_lex(); + type = evaluate_expression(1, TYPE_8, 0); + if (target == CPU_Z80) + cpuz80_2op("LD", "(fm_inst+2)", "A"); + if (lex != C_COMMA) + emit_error("missing comma"); + else + get_lex(); + type = evaluate_expression(1, TYPE_8, 0); + if (target == CPU_Z80) + cpuz80_2op("LD", "(fm_inst+3)", "A"); } + } else { + struct label *label; + + label = label_search(name); + if (label == NULL) { + label = label_add(name); + } + label->used |= LABEL_USED; if (target == CPU_6502) { - cpu6502_1op("LDA", "temp"); - cpu6502_1op("ASL", "A"); - cpu6502_1op("ROL", "temp+1"); - cpu6502_noop("CLC"); - sprintf(temp, "#" INTERNAL_PREFIX "%d", table); - cpu6502_1op("ADC", temp); - cpu6502_1op("STA", "temp"); - cpu6502_1op("LDA", "temp+1"); + strcpy(temp, "#" LABEL_PREFIX); + strcat(temp, name); + cpu6502_1op("LDA", temp); strcat(temp, ">>8"); - cpu6502_1op("ADC", temp); - cpu6502_1op("STA", "temp+1"); - cpu6502_1op("LDY", "#0"); - cpu6502_1op("LDA", "(temp),Y"); - cpu6502_1op("STA", "temp2"); - cpu6502_noop("INY"); - cpu6502_1op("LDA", "(temp),Y"); - cpu6502_1op("STA", "temp2+1"); - cpu6502_1op("JMP", "(temp2)"); + cpu6502_1op("LDY", temp); } else if (target == CPU_9900) { - if ((type & MAIN_TYPE) == TYPE_8) { - cpu9900_2op("srl", "r0", "8"); + strcpy(temp, LABEL_PREFIX); + strcat(temp, name); + cpu9900_2op("li", "r0", temp); + } else { + strcpy(temp, LABEL_PREFIX); + strcat(temp, name); + cpuz80_2op("LD", "HL", temp); + } + generic_call("music_play"); + get_lex(); + } + } else if (strcmp(name, "MUSIC") == 0) { + int arg; + static int previous[4]; + unsigned int notes; + int note; + int c; + + generic_dump(); + get_lex(); + notes = 0; + arg = 0; + while (1) { + if (lex != C_NAME && lex != C_MINUS) { + emit_error("bad syntax for MUSIC"); + break; + } + if (lex == C_MINUS) { + /* Nothing to do */ + } else if (arg == 0 && strcmp(name, "REPEAT") == 0) { + get_lex(); + notes = 0xfd; + break; + } else if (arg == 0 && strcmp(name, "STOP") == 0) { + get_lex(); + notes = 0xfe; + break; + } else if (arg == 3) { + if (name[0] != 'M' || name[1] < '1' || name[1] > '3') { + emit_error("bad syntax for drum in MUSIC"); + break; } - cpu9900_2op("sla", "r0", "1"); - cpu9900_2op("mov", "r0", "r1"); - sprintf(temp, "@" INTERNAL_PREFIX "%d(r1)", table); - cpu9900_2op("mov", temp, "r0"); - cpu9900_1op("b", "*r0"); + notes |= (name[1] - '0') << ((arg & 3) * 8); + } else if (strcmp(name, "S") == 0) { + notes |= 0x3f << ((arg & 3) * 8); } else { - if ((type & MAIN_TYPE) == TYPE_8) { - cpuz80_2op("LD", "L", "A"); - if (type & TYPE_SIGNED) { - cpuz80_noop("RLA"); - cpuz80_2op("SBC", "A", "A"); - cpuz80_2op("LD", "H", "A"); - } else { - cpuz80_2op("LD", "H", "0"); - } + notes |= previous[arg] << ((arg & 3) * 8); + c = 0; + switch (name[c++]) { + case 'C': note = 0; break; + case 'D': note = 2; break; + case 'E': note = 4; break; + case 'F': note = 5; break; + case 'G': note = 7; break; + case 'A': note = 9; break; + case 'B': note = 11; break; + default: + note = 0; + emit_error("bad syntax for note in MUSIC"); + break; } - cpuz80_2op("ADD", "HL", "HL"); - sprintf(temp, INTERNAL_PREFIX "%d", table); - cpuz80_2op("LD", "DE", temp); - cpuz80_2op("ADD", "HL", "DE"); - cpuz80_2op("LD", "A", "(HL)"); - cpuz80_1op("INC", "HL"); - cpuz80_2op("LD", "H", "(HL)"); - cpuz80_2op("LD", "L", "A"); - cpuz80_1op("JP", "(HL)"); - } - sprintf(temp, INTERNAL_PREFIX "%d", table); - generic_label(temp); - for (c = 0; c < max_value; c++) { - if (options[c] != NULL) { - sprintf(temp, LABEL_PREFIX "%s", options[c]->name); - } else { - sprintf(temp, INTERNAL_PREFIX "%d", new_label); + switch (name[c++]) { + case '2': note += 0 * 12; break; + case '3': note += 1 * 12; break; + case '4': note += 2 * 12; break; + case '5': note += 3 * 12; break; + case '6': note += 4 * 12; break; + case '7': if (note == 0) { note += 5 * 12; break; } + default: + emit_error("bad syntax for note in MUSIC"); + break; } - if (target == CPU_6502) - cpu6502_1op("DW", temp); - else if (target == CPU_9900) - cpu9900_1op("data", temp); - else - cpuz80_1op("DW", temp); + note++; + if (name[c] == '#') { + note++; + c++; + } + if (name[c] == 'W') { + previous[arg] = 0x00; + notes &= ~(0xc0 << ((arg & 3) * 8)); + notes |= previous[arg] << ((arg & 3) * 8); + } else if (name[c] == 'X') { + previous[arg] = 0x40; + notes &= ~(0xc0 << ((arg & 3) * 8)); + notes |= previous[arg] << ((arg & 3) * 8); + } else if (name[c] == 'Y') { + previous[arg] = 0x80; + notes &= ~(0xc0 << ((arg & 3) * 8)); + notes |= previous[arg] << ((arg & 3) * 8); + } else if (name[c] == 'Z') { + previous[arg] = 0xc0; + notes &= ~(0xc0 << ((arg & 3) * 8)); + notes |= previous[arg] << ((arg & 3) * 8); + } + notes |= note << ((arg & 3) * 8); } - sprintf(temp, INTERNAL_PREFIX "%d", new_label); - generic_label(temp); + get_lex(); + arg++; + if (lex != C_COMMA) + break; + if (arg == 4) { + emit_error("too many arguments for MUSIC"); + break; + } + get_lex(); + } + if (target == CPU_9900) { + fprintf(output, "\tbyte >%02x,>%02x,>%02x,>%02x\n", notes & 0xff, (notes >> 8) & 0xff, (notes >> 16) & 0xff, (notes >> 24) & 0xff); + } else { + fprintf(output, "\tdb $%02x,$%02x,$%02x,$%02x\n", notes & 0xff, (notes >> 8) & 0xff, (notes >> 16) & 0xff, (notes >> 24) & 0xff); } } else if (strcmp(name, "SOUND") == 0) { get_lex(); if (lex != C_NUM) { emit_error("syntax error in SOUND"); } else { - if (value < 3 && (machine == MSX || machine == SVI || machine == EINSTEIN || machine == NABU)) + if (value < 10 && machine == NES) { + emit_warning("using SOUND 0-9 with NES/Famicom target"); + } else if (value >= 0 && value <= 4 && (machine == MSX || machine == MSX2 || machine == SVI || machine == EINSTEIN || machine == NABU)) { emit_warning("using SOUND 0-3 with AY-3-8910 target"); - else if (value >= 5 && machine != MSX && machine != COLECOVISION_SGM && machine != SVI && machine != SORD && machine != MEMOTECH) + } else if (value >= 5 && value <= 9 && machine != MSX && machine != MSX2 && machine != COLECOVISION_SGM && machine != SVI && machine != SORD && machine != MEMOTECH && machine != NABU) { emit_warning("using SOUND 5-9 with SN76489 target"); + } switch (value) { + /* Sound handling for SN76489 */ case 0: get_lex(); if (lex != C_COMMA) { @@ -4890,6 +5895,7 @@ void compile_statement(int check_for_else) generic_call("sn76489_vol"); } break; + /* Sound handling for AY-3-8910 */ case 5: get_lex(); if (lex != C_COMMA) { @@ -5044,7 +6050,7 @@ void compile_statement(int check_for_else) if (machine == EINSTEIN || machine == NABU) { cpuz80_1op("OR", "$40"); } else { - /* Protect these MSX machines! */ + /* Protect these MSX1/MSX2 machines! */ cpuz80_1op("OR", "$80"); } cpuz80_2op("LD", "B", "$07"); @@ -5052,6 +6058,124 @@ void compile_statement(int check_for_else) } } break; + /* Sound handling for NES */ + case 10: + get_lex(); + if (lex != C_COMMA) { + emit_error("missing comma in sound"); + } else { + get_lex(); + } + if (lex != C_COMMA) { + type = evaluate_expression(1, TYPE_16, 0); + if (machine == NES) { + cpu6502_1op("STA", "$4002"); + cpu6502_1op("STY", "$4003"); + } + } + if (lex == C_COMMA) { + get_lex(); + type = evaluate_expression(1, TYPE_8, 0); + if (machine == NES) { + cpu6502_1op("STA", "$4000"); + } + } + if (lex == C_COMMA) { + get_lex(); + type = evaluate_expression(1, TYPE_8, 0); + if (machine == NES) { + cpu6502_1op("STA", "$4001"); + } + } + break; + case 11: + get_lex(); + if (lex != C_COMMA) { + emit_error("missing comma in sound"); + } else { + get_lex(); + } + if (lex != C_COMMA) { + type = evaluate_expression(1, TYPE_16, 0); + if (machine == NES) { + cpu6502_1op("STA", "$4006"); + cpu6502_1op("STY", "$4007"); + } + } + if (lex == C_COMMA) { + get_lex(); + type = evaluate_expression(1, TYPE_8, 0); + if (machine == NES) { + cpu6502_1op("STA", "$4004"); + } + } + if (lex == C_COMMA) { + get_lex(); + type = evaluate_expression(1, TYPE_8, 0); + if (machine == NES) { + cpu6502_1op("STA", "$4005"); + } + } + break; + case 12: + get_lex(); + if (lex != C_COMMA) { + emit_error("missing comma in sound"); + } else { + get_lex(); + } + if (lex != C_COMMA) { + type = evaluate_expression(1, TYPE_16, 0); + if (machine == NES) { + cpu6502_1op("STA", "$400a"); + cpu6502_1op("STY", "$400b"); + } + } + if (lex == C_COMMA) { + get_lex(); + type = evaluate_expression(1, TYPE_8, 0); + if (machine == NES) { + cpu6502_1op("STA", "$4008"); + } + } + break; + case 14: + get_lex(); + if (lex != C_COMMA) { + emit_error("missing comma in sound"); + } else { + get_lex(); + } + if (lex != C_COMMA) { + type = evaluate_expression(1, TYPE_16, 0); + if (machine == NES) { + cpu6502_1op("STA", "$400e"); + cpu6502_1op("STY", "$400f"); + } + } + if (lex == C_COMMA) { + get_lex(); + type = evaluate_expression(1, TYPE_8, 0); + if (machine == NES) { + cpu6502_1op("STA", "$400c"); + } + } + break; + case 15: + get_lex(); + if (lex != C_COMMA) { + emit_error("missing comma in sound"); + } else { + get_lex(); + } + type = evaluate_expression(1, TYPE_8, 0); + if (machine == NES) { + cpu6502_1op("STA", "$4015"); + } + break; + default: + emit_error("SOUND with unhandled channel number"); + break; } } } else if (strcmp(name, "CALL") == 0) { /* Call assembly language */ @@ -5082,129 +6206,6 @@ void compile_statement(int check_for_else) fprintf(output, "%s\n", &line[line_pos]); line_pos = line_size; get_lex(); - } else if (strcmp(name, "DEF") == 0) { /* Function definition (macro in CVBasic) */ - char function[MAX_LINE_SIZE]; - int total_arguments; - char *arguments[32]; - struct macro *macro; - int c; - - get_lex(); - if (lex != C_NAME || strcmp(name, "FN") != 0) { - emit_error("syntax error for DEF FN"); - } else { - get_lex(); - if (lex != C_NAME) { - emit_error("missing function name for DEF FN"); - } else if (macro_search(name) != NULL) { - emit_error("DEF FN name already defined"); - } else { - strcpy(function, name); - get_lex(); - total_arguments = 0; - if (lex == C_LPAREN) { - get_lex(); - while (1) { - if (lex != C_NAME) { - emit_error("syntax error in argument list for DEF FN"); - break; - } - if (total_arguments == 32) { - emit_error("More than 32 arguments in DEF FN"); - break; - } - arguments[total_arguments] = malloc(strlen(name) + 1); - if (arguments[total_arguments] == NULL) { - emit_error("Out of memory in DEF FN"); - break; - } - strcpy(arguments[total_arguments], name); - total_arguments++; - get_lex(); - if (lex == C_COMMA) { - get_lex(); - } else if (lex == C_RPAREN) { - get_lex(); - break; - } else { - emit_error("syntax error in argument list for DEF FN"); - break; - } - } - } - macro = macro_add(function); - macro->total_arguments = total_arguments; - if (lex != C_EQUAL) { - emit_error("missing = in DEF FN"); - } else { - get_lex(); - while (lex != C_END) { - if (lex == C_ERR) { - emit_error("bad syntax inside DEF FN replacement text"); - break; - } - if (lex == C_NAME) { - for (c = 0; c < total_arguments; c++) { - if (strcmp(arguments[c], name) == 0) - break; - } - if (c < total_arguments) { - lex = C_ERR; - value = c; - name[0] = '\0'; - } - } - if (macro->length >= macro->max_length) { - macro->definition = realloc(macro->definition, (macro->max_length + 1) * 2 * sizeof(struct macro_def)); - if (macro->definition == NULL) { - emit_error("Out of memory in DEF FN"); - break; - } - macro->max_length = (macro->max_length + 1) * 2; - } - macro->definition[macro->length].lex = lex; - macro->definition[macro->length].value = value; - macro->definition[macro->length].name = malloc(strlen(name) + 1); - if (macro->definition[macro->length].name == NULL) { - emit_error("Out of memory in DEF FN"); - break; - } - strcpy(macro->definition[macro->length].name, name); - macro->length++; - get_lex(); - } - } - } - } - } else if (strcmp(name, "OPTION") == 0) { - get_lex(); - if (lex != C_NAME) { - emit_error("required name after OPTION"); - } else if (strcmp(name, "EXPLICIT") == 0) { - get_lex(); - if (lex == C_NAME && strcmp(name, "ON") == 0) { - get_lex(); - option_explicit = 1; - } else if (lex == C_NAME && strcmp(name, "OFF") == 0) { - get_lex(); - option_explicit = 0; - } else { - option_explicit = 1; - } - } else if (strcmp(name, "WARNINGS") == 0) { - get_lex(); - if (lex == C_NAME && strcmp(name, "ON") == 0) { - get_lex(); - option_warnings = 1; - } else if (lex == C_NAME && strcmp(name, "OFF") == 0) { - get_lex(); - option_warnings = 0; - } else { - emit_error("missing ON/OFF in OPTION WARNINGS"); - } - } else { - emit_error("non-recognized OPTION"); - } } else if (strcmp(name, "BANK") == 0) { get_lex(); if (lex == C_NAME && strcmp(name, "ROM") == 0) { @@ -5212,7 +6213,7 @@ void compile_statement(int check_for_else) if (lex != C_NUM) { emit_error("Bad syntax for BANK ROM"); } else if (value != 128 && value != 256 && value != 512 && value != 1024) { - // TODO: TI can do 2MB as it stands, and 32MB with the scheme. But leaving at 1MB for now. + /* TODO: TI can do 2MB as it stands, and 32MB with the scheme. But leaving at 1MB for now. */ emit_error("BANK ROM not 128, 256, 512 or 1024"); get_lex(); } else if (bank_switching != 0) { @@ -5221,6 +6222,8 @@ void compile_statement(int check_for_else) } else { if (machine == SVI || machine == SORD || machine == MEMOTECH || machine == CREATIVISION || machine == EINSTEIN || machine == PV2000) { emit_error("Bank-switching not supported with current platform"); + } else if (machine == NES && (value == 128 || value == 1024)) { + emit_error("NES/Famicom only supports 256 or 512 k ROM size"); } else { bank_switching = 1; bank_rom_size = value; @@ -5237,6 +6240,7 @@ void compile_statement(int check_for_else) tree = evaluate_level_0(&type); if (tree->type != N_NUM8 && tree->type != N_NUM16) { emit_error("not a constant expression in BANK SELECT"); + node_delete(tree); break; } c = tree->value; @@ -5245,8 +6249,8 @@ void compile_statement(int check_for_else) emit_error("Using BANK SELECT without BANK ROM"); } else { if (machine == TI994A) { - // the TI needs to use 8k banks, so our masks are different - c+=2; // reserving 3 banks (0,1,2) for 'fixed' space + /* the TI needs to use 8k banks, so our masks are different */ + c += 2; /* reserving 3 banks (0,1,2) for 'fixed' space */ if (bank_rom_size == 128) c &= 0x0f; @@ -5257,28 +6261,50 @@ void compile_statement(int check_for_else) else c &= 0x7f; - c = 0x6000+(c*2); // ROM address to poke + c = 0x6000+(c*2); /* ROM address to poke */ sprintf(temp, "@>%x", c); cpu9900_1op("clr", temp); } else { if (machine == COLECOVISION || machine == COLECOVISION_SGM) c--; - if (bank_rom_size == 128) - c &= 0x07; - else if (bank_rom_size == 256) - c &= 0x0f; - else if (bank_rom_size == 512) - c &= 0x1f; - else - c &= 0x3f; - if (machine == SG1000) { - sprintf(temp, "%d", c & 0x3f); + if (machine == NES) { + c = (c & 0xe0) | ((c - 1) & 0x1f); + if ((bank_rom_size == 256 && (c & 0x0f) >= 0x0d) || (bank_rom_size == 512 && (c & 0x1f) >= 0x1d)) { + emit_warning("Selecting bank reserved for CHRROM data"); + } + } else { + if (bank_rom_size == 128) + c &= 0x07; + else if (bank_rom_size == 256) + c &= 0x0f; + else if (bank_rom_size == 512) + c &= 0x1f; + else + c &= 0x3f; + } + if (machine == SG1000 || machine == SMS) { + sprintf(temp, "%d", c); cpuz80_2op("LD", "A", temp); cpuz80_2op("LD", "($fffe)", "A"); - } else if (machine == MSX) { - sprintf(temp, "%d", c & 0x3f); - cpuz80_2op("LD", "A", temp); - cpuz80_2op("LD", "($7000)", "A"); + } else if (machine == MSX || machine == MSX2) { + if (bank_konami) { + sprintf(temp, "%d", c * 2); + cpuz80_noop("DI"); + cpuz80_2op("LD", "A", temp); + cpuz80_2op("LD", "($8000)", "A"); + cpuz80_1op("INC", "A"); + cpuz80_2op("LD", "($a000)", "A"); + cpuz80_noop("EI"); + } else { + sprintf(temp, "%d", c); + cpuz80_2op("LD", "A", temp); + cpuz80_2op("LD", "($7000)", "A"); + } + } else if (machine == NES) { + sprintf(temp, "#%d", c); + cpu6502_1op("LDA", temp); + cpu6502_1op("ORA", "CHRRAM_BANK"); + cpu6502_1op("STA", "BANKSEL"); } else { if (bank_rom_size == 128) c |= 0xfff8; @@ -5301,7 +6327,8 @@ void compile_statement(int check_for_else) tree = evaluate_level_0(&type); if (tree->type != N_NUM8 && tree->type != N_NUM16) { - emit_error("not a constant expression in BANK SELECT"); + emit_error("not a constant expression in BANK"); + node_delete(tree); break; } c = tree->value; @@ -5311,8 +6338,8 @@ void compile_statement(int check_for_else) } else { d = c; if (machine == TI994A) { - // the TI needs to use 8k banks, so our masks are different - c+=2; // reserving 3 banks (0,1,2) for 'fixed' space + /* the TI needs to use 8k banks, so our masks are different */ + c += 2; /* reserving 3 banks (0,1,2) for 'fixed' space */ if (bank_rom_size == 128) c &= 0x0f; @@ -5328,10 +6355,10 @@ void compile_statement(int check_for_else) cpu9900_1op("bank", temp); cpu9900_empty(); } else { - if (machine == COLECOVISION || machine == COLECOVISION_SGM) + if (machine == COLECOVISION || machine == COLECOVISION_SGM || machine == NES) c--; if (machine == TI994A) - c+=2; // reserving 3 banks (0,1,2) for 'fixed' space + c += 2; /* reserving 3 banks (0,1,2) for 'fixed' space */ if (bank_rom_size == 128) c &= 0x07; else if (bank_rom_size == 256) @@ -5341,13 +6368,25 @@ void compile_statement(int check_for_else) else c &= 0x3f; bank_finish(); - sprintf(temp, "$%05x", c << 14); - cpuz80_1op("FORG", temp); - if (machine == SG1000) { + if (machine == NES) { + if ((bank_rom_size == 256 && c >= 0x0d) || (bank_rom_size == 512 && c >= 0x1d)) { + emit_error("Using bank reserved for CHRROM data"); + } + sprintf(temp, "$%05x", (c << 14) + 0x0010); + } else { + sprintf(temp, "$%05x", c << 14); + } + if (machine == SG1000 || machine == SMS) { + cpuz80_1op("FORG", temp); cpuz80_1op("ORG", "$4000"); - } else if (machine == MSX) { + } else if (machine == MSX || machine == MSX2) { + cpuz80_1op("FORG", temp); cpuz80_1op("ORG", "$8000"); + } else if (machine == NES) { + cpu6502_1op("FORG", temp); + cpu6502_1op("ORG", "$8000"); } else { + cpuz80_1op("FORG", temp); cpuz80_1op("ORG", "$c000"); } cpuz80_empty(); @@ -5365,6 +6404,7 @@ void compile_statement(int check_for_else) get_lex(); if (lex != C_NUM) { emit_error("Not a constant in VDP"); + vdp_reg = 0; } else { vdp_reg = value; get_lex(); @@ -5379,11 +6419,15 @@ void compile_statement(int check_for_else) get_lex(); type = evaluate_expression(1, TYPE_8, 0); if (target == CPU_6502) { - sprintf(temp, "#%d", vdp_reg); - cpu6502_1op("LDX", temp); - cpu6502_noop("SEI"); - cpu6502_1op("JSR", "WRTVDP"); - cpu6502_noop("CLI"); + if (machine == NES) { + emit_error("VDP not supported for NES/Famicom"); + } else { + sprintf(temp, "#%d", vdp_reg); + cpu6502_1op("LDX", temp); + generic_interrupt_disable(); + cpu6502_1op("JSR", "WRTVDP"); + generic_interrupt_enable(); + } } else if (target == CPU_9900) { /* Simpler to do inline */ sprintf(temp, "%d ; %d*256+0x8000", vdp_reg*256+0x8000, vdp_reg); @@ -5398,9 +6442,9 @@ void compile_statement(int check_for_else) cpuz80_2op("LD", "B", "A"); sprintf(temp, "%d", vdp_reg); cpuz80_2op("LD", "C", temp); - cpuz80_1op("CALL", "nmi_off"); + generic_interrupt_disable(); cpuz80_1op("CALL", "WRTVDP"); - cpuz80_1op("CALL", "nmi_on"); + generic_interrupt_enable(); } } else if (macro_search(name) != NULL) { /* Function (macro) */ if (!replace_macro()) { @@ -5428,7 +6472,9 @@ void compile_basic(void) struct label *label; int label_exists; char *p; - + int skip = 0; + int if_depth = 0; + current_line = 0; while (fgets(line, sizeof(line) - 1, input)) { current_line++; @@ -5442,11 +6488,97 @@ void compile_basic(void) generic_dump(); fprintf(output, "\t; %s\n", line); + /* For debugging purposes */ +/* fprintf(stderr, "%s\n", line);*/ + line_start = 1; line_pos = 0; label_exists = 0; label = NULL; get_lex(); + + /* + ** C-style preprocessor #IF, #ELSE, #ENDIF + ** Currently supports a single nesting level. + */ + if (lex == C_NAME && name[0] == '#') { + int inv = 0; + + if (strcmp(name, "#IF") == 0 || strcmp(name, "#ELIF") == 0) { + if (name[1] == 'I') { /* #IF */ + if (if_depth) { + emit_error("Nested #IF not supported"); + } + } else { /* #ELIF */ + if (!if_depth) { + emit_error("#ELIF without matching #IF"); + } + if (if_depth < 0 || !skip) { + if_depth = -1; + skip = 1; + continue; + } + } + get_lex(); + if (lex == C_NAME) { + if (strcmp(name, "NOT") == 0) { + get_lex(); + inv = -1; + } + } + if_depth = 1; + if (lex == C_NAME) { + struct constant *c = constant_search(name); + + skip = (!c || (c->value == 0)); + } else if (lex == C_NUM) { + skip = (value == 0); + } + get_lex(); + if (lex != C_END) { + emit_error("Extra characters"); + } + } else if (strcmp(name, "#ENDIF") == 0) { + if (!if_depth) { + emit_error("#ENDIF without matching #IF"); + } + if_depth = 0; + skip = 0; + continue; + } else if (strcmp(name, "#ELSE") == 0) { + if (if_depth > 0) { + skip = !skip; + } else if (if_depth < 0) { + skip = 1; + } else { + emit_error("Unexpected #ELSE. No matching #IF"); + } + continue; + } + if (inv) + skip = !skip; + } + + if (skip) + continue; + + if (lex == C_NAME && name[0] == '#') { + if (strcmp(name, "#INFO") == 0) { + get_lex(); + emit_info(name); + continue; + } else if (strcmp(name, "#WARNING") == 0) { + get_lex(); + emit_warning(name); + continue; + } else if (strcmp(name, "#ERROR") == 0) { + get_lex(); + emit_error(name); + continue; + } + } + + if (lex == C_LABEL) { if (value == 0) strcpy(global_label, name); @@ -5461,11 +6593,29 @@ void compile_basic(void) } else { label = label_add(name); } + + /* first build up the label into temp */ label->used |= LABEL_DEFINED; strcpy(temp, LABEL_PREFIX); strcat(temp, name); - generic_label(temp); + + /* + ** We can look ahead a little - get_lex won't mess with temp[] + ** and we can use it to tell if the TI port needs to output an EVEN directive. + */ get_lex(); + + if (target == CPU_9900 && lex == C_NAME && strcmp(name, "PROCEDURE") == 0) { + /* + ** code MUST be even aligned + ** this won't trigger for inline labels, but I don't + ** think inline labels can become misaligned? + */ + cpu9900_noop("even"); + } + + /* Now we can emit the label. Remember, we already get_lex'd */ + generic_label(temp); label_exists = 1; } if (lex == C_NAME) { @@ -5555,34 +6705,49 @@ int process_variables(void) int size; int address; - if (machine == CREATIVISION || machine == TI994A) - address = consoles[machine].base_ram; + address = consoles[machine].base_ram; /* Only Creativision, NES and TI994A */ bytes_used = 0; for (c = 0; c < HASH_PRIME; c++) { label = label_hash[c]; while (label != NULL) { if ((label->used & (LABEL_CALLED_BY_GOTO & LABEL_IS_PROCEDURE)) == (LABEL_CALLED_BY_GOTO | LABEL_IS_PROCEDURE)) { fprintf(stderr, "Error: PROCEDURE '%s' jumped in by GOTO\n", label->name); - err_code = 1; + err_code = EXIT_FAILURE; } if ((label->used & (LABEL_CALLED_BY_GOSUB & LABEL_IS_PROCEDURE)) == LABEL_CALLED_BY_GOSUB) { fprintf(stderr, "Error: Common label '%s' jumped in by GOSUB\n", label->name); - err_code = 1; + err_code = EXIT_FAILURE; } if (label->used & LABEL_IS_VARIABLE) { if (target == CPU_6502) { - if ((label->used & MAIN_TYPE) == TYPE_8) { - if (address < 0x0200 && address + 1 > 0x0140) - address = 0x0200; - sprintf(temp, "%s%s:\tequ $%04x", LABEL_PREFIX, label->name, address); - address++; - bytes_used++; + if (machine == NES) { + if ((label->used & MAIN_TYPE) == TYPE_8) { + if (address < 0x0300 && address + 1 > 0x0140) + address = 0x0300; + sprintf(temp, "%s%s:\tequ $%04x", LABEL_PREFIX, label->name, address); + address++; + bytes_used++; + } else { + if (address < 0x0300 && address + 2 > 0x0140) + address = 0x0300; + sprintf(temp, "%s%s:\tequ $%04x", LABEL_PREFIX, label->name, address); + address += 2; + bytes_used += 2; + } } else { - if (address < 0x0200 && address + 2 > 0x0140) - address = 0x0200; - sprintf(temp, "%s%s:\tequ $%04x", LABEL_PREFIX, label->name, address); - address += 2; - bytes_used += 2; + if ((label->used & MAIN_TYPE) == TYPE_8) { + if (address < 0x0200 && address + 1 > 0x0140) + address = 0x0200; + sprintf(temp, "%s%s:\tequ $%04x", LABEL_PREFIX, label->name, address); + address++; + bytes_used++; + } else { + if (address < 0x0200 && address + 2 > 0x0140) + address = 0x0200; + sprintf(temp, "%s%s:\tequ $%04x", LABEL_PREFIX, label->name, address); + address += 2; + bytes_used += 2; + } } fprintf(output, "%s\n", temp); } else if (target == CPU_9900) { @@ -5640,8 +6805,13 @@ int process_variables(void) size = 1; size *= label->length; if (target == CPU_6502) { - if (address < 0x0200 && address + size > 0x0140) - address = 0x0200; + if (machine == NES) { + if (address < 0x0300 && address + size > 0x0140) + address = 0x0300; + } else { + if (address < 0x0200 && address + size > 0x0140) + address = 0x0200; + } sprintf(temp, ARRAY_PREFIX "%s:\tequ $%04x", label->name, address); address += size; fprintf(output, "%s\n", temp); @@ -5676,10 +6846,8 @@ int process_variables(void) */ int main(int argc, char *argv[]) { - struct label *label; FILE *prologue; int c; - int size; char *p; char *p1; int bytes_used; @@ -5691,12 +6859,13 @@ int main(int argc, char *argv[]) int cpm_option; int pencil; char hex; + struct constant *machine_constant; actual = time(0); date = localtime(&actual); fprintf(stderr, "\nCVBasic compiler " VERSION "\n"); - fprintf(stderr, "(c) 2024 Oscar Toledo G. https://nanochess.org/\n\n"); + fprintf(stderr, "(c) 2024-2026 Oscar Toledo G. https://nanochess.org/\n\n"); if (argc < 3) { fprintf(stderr, "Usage:\n"); @@ -5704,7 +6873,7 @@ int main(int argc, char *argv[]) machine = COLECOVISION; while (machine < TOTAL_TARGETS) { if (machine == COLECOVISION) - fprintf(stderr, " cvbasic input.bas output.asm [library_path]\n"); + fprintf(stderr, " cvbasic [-DCONST=5] input.bas output.asm [library_path]\n"); else fprintf(stderr, " cvbasic --%s input.bas output.asm [library_path]\n", consoles[machine].name); if (consoles[machine].options[0]) @@ -5723,12 +6892,13 @@ int main(int argc, char *argv[]) fprintf(stderr, "\n"); fprintf(stderr, " It will return a zero error code if compilation was\n"); fprintf(stderr, " successful, or non-zero otherwise.\n\n"); - fprintf(stderr, "Many thanks to Albert, abeker, aotta, artrag, atari2600land,\n"); - fprintf(stderr, "carlsson, chalkyw64, CrazyBoss, drfloyd, gemintronic, Jess Ragan,\n"); - fprintf(stderr, "Kamshaft, Kiwi, MADrigal, pixelboy, SiRioKD, Tarzilla,\n"); - fprintf(stderr, "Tony Cruise, tursilion, visrealm, wavemotion, and youki.\n"); + fprintf(stderr, "Many thanks to acadiel, Albert, abeker, an3ss, aotta, artrag,\n"); + fprintf(stderr, "atari2600land, carlsson, chalkyw64, CrazyBoss, drfloyd, gemintronic,\n"); + fprintf(stderr, "Jess Ragan, Kamshaft, Kiwi, MADrigal, pixelboy, Revontuli, SiRioKD,\n"); + fprintf(stderr, "Tarzilla, Tony Cruise, tursilion, unhuman, visrealm, wavemotion,\n"); + fprintf(stderr, "and youki.\n"); fprintf(stderr, "\n"); - exit(1); + exit(EXIT_FAILURE); } /* @@ -5750,7 +6920,7 @@ int main(int argc, char *argv[]) } if (machine == TOTAL_TARGETS) { fprintf(stderr, "Unknown target: %s\n", argv[c]); - exit(1); + exit(EXIT_FAILURE); } target = consoles[machine].target; c++; @@ -5765,66 +6935,135 @@ int main(int argc, char *argv[]) } else { pencil = 0; } - + bytes_used = 0; + /* ** Extra options. */ extra_ram = 0; - if (argv[c][0] == '-' && tolower(argv[c][1]) == 'r' && tolower(argv[c][2] == 'a') && - tolower(argv[c][3] == 'm') && argv[c][4] == '1' && argv[c][5] == '6' && + if (argv[c][0] == '-' && tolower(argv[c][1]) == 'r' && tolower(argv[c][2]) == 'a' && + tolower(argv[c][3]) == 'm' && argv[c][4] == '1' && argv[c][5] == '6' && argv[c][6] == '\0') { c++; - if (machine == MSX) { + if (machine == MSX || machine == MSX2) { extra_ram = 8192; } else { - fprintf(stderr, "-ram16 option only applies to MSX.\n"); - exit(2); + fprintf(stderr, "-ram16 option only applies to MSX/MSX2.\n"); + exit(EXIT_FAILURE + 1); + } + } + bank_konami = 0; + if (argv[c][0] == '-' && tolower(argv[c][1]) == 'k' && tolower(argv[c][2]) == 'o' && + tolower(argv[c][3]) == 'n' && tolower(argv[c][4]) == 'a' && tolower(argv[c][5]) == 'm' && tolower(argv[c][6]) == 'i' && + argv[c][7] == '\0') { + c++; + if (machine == MSX || machine == MSX2) { + bank_konami = 1; + } else { + fprintf(stderr, "-konami option only applies to MSX/MSX2.\n"); + exit(EXIT_FAILURE + 1); } } cpm_option = 0; if (machine == EINSTEIN) cpm_option = 1; /* Forced */ - if (argv[c][0] == '-' && tolower(argv[c][1]) == 'c' && tolower(argv[c][2] == 'p') && - tolower(argv[c][3] == 'm') && argv[c][4] == '\0') { + if (argv[c][0] == '-' && tolower(argv[c][1]) == 'c' && tolower(argv[c][2]) == 'p' && + tolower(argv[c][3]) == 'm' && argv[c][4] == '\0') { c++; if (machine == MEMOTECH || machine == NABU) { cpm_option = 1; } else { fprintf(stderr, "-cpm option only applies to Memotech or NABU.\n"); - exit(2); + exit(EXIT_FAILURE + 1); } } small_rom = 0; - if (argv[c][0] == '-' && tolower(argv[c][1]) == 'r' && tolower(argv[c][2] == 'o') && - tolower(argv[c][3] == 'm') && argv[c][4] == '1' && argv[c][5] == '6' && + if (argv[c][0] == '-' && tolower(argv[c][1]) == 'r' && tolower(argv[c][2]) == 'o' && + tolower(argv[c][3]) == 'm' && argv[c][4] == '1' && argv[c][5] == '6' && argv[c][6] == '\0') { c++; if (machine == CREATIVISION) { small_rom = 1; } else { fprintf(stderr, "-rom16 option only applies to Creativision.\n"); - exit(2); + exit(EXIT_FAILURE + 1); + } + } + + /* + ** Create machine constant + */ + machine_constant = constant_add(consoles[machine].constant);; + machine_constant->value = 1; + + /* + ** Passed-in constants + */ + while (argv[c][0] == '-' && tolower(argv[c][1]) == 'd') { + int i = 1; + char ch = 0; + char *p = name; + struct constant *d = NULL; + + while (1) { + ch = argv[c][++i]; + if (ch == '\0') + break; + if (!isalnum(ch) && ch != '_' && ch != '#' && ch != '=') { + fprintf(stderr, "%s name includes invalid characters.\n", argv[c]); + exit(EXIT_FAILURE + 1); + } + if (ch == '=') { + *p = '\0'; + d = constant_search(name); + if (d != NULL) { + fprintf(stderr, "constant redefined %s\n", argv[c]); + } else { + d = constant_add(name); + d->value = atoi(&argv[c][i + 1]); + } + break; + } else { + if (p < name + sizeof(name) - 1) + *p++ = toupper(ch); + } } + if (d == NULL) { + fprintf(stderr, "%s missing assignment. Syntax: -DCONST=123 -D#BIGCONST=12345\n", argv[c]); + exit(EXIT_FAILURE + 1); + } + c++; } + + /* + ** Here is compiled the source code and generates a temporary assembler file. + */ strcpy(current_file, argv[c]); - err_code = 0; + err_code = EXIT_SUCCESS; input = fopen(current_file, "r"); if (input == NULL) { fprintf(stderr, "Couldn't open '%s' source file.\n", current_file); - exit(2); + exit(EXIT_FAILURE + 1); } c++; output = fopen(TEMPORARY_ASSEMBLER, "w"); if (output == NULL) { fprintf(stderr, "Couldn't open '%s' temporary file.\n", TEMPORARY_ASSEMBLER); - exit(2); + exit(EXIT_FAILURE + 1); } bank_switching = 0; option_explicit = 0; option_warnings = 1; inside_proc = NULL; frame_drive = NULL; + + current_chrrom = -1; /* Only NES */ + chrrom_pointer = 0; /* Only NES */ + chrrom_size = 0; /* Only NES */ + chrrom_data = NULL; /* Only NES */ + nes_nametable = 0; /* Only NES */ + compile_basic(); if (loops != NULL) emit_error("End of source with control block still open"); @@ -5840,10 +7079,13 @@ int main(int argc, char *argv[]) fclose(input); fclose(output); + /* + ** Now build the real output (prologue + compiled program + epilogue) + */ output = fopen(argv[c], "w"); if (output == NULL) { fprintf(stderr, "Couldn't open '%s' output file.\n", argv[2]); - exit(2); + exit(EXIT_FAILURE + 1); } c++; @@ -5882,7 +7124,17 @@ int main(int argc, char *argv[]) fprintf(output, "COLECO:\tequ %d\n", (machine == COLECOVISION || machine == COLECOVISION_SGM) ? 1 : 0); fprintf(output, "SG1000:\tequ %d\n", (machine == SG1000) ? 1 : 0); - fprintf(output, "MSX:\tequ %d\n", (machine == MSX) ? 1 : 0); + if (machine == MSX) + c = 1; + else if (machine == MSX2) + c = 2; + else + c = 0; + fprintf(output, "MSX:\tequ %d\n", c); + if (c) { + fprintf(output, "KONAMI:\tequ %d\n", bank_konami); + } + fprintf(output, "FM_SUPPORT:\tequ %d\n", option_fm); fprintf(output, "SGM:\tequ %d\n", (machine == COLECOVISION_SGM) ? 1 : 0); fprintf(output, "SVI:\tequ %d\n", (machine == SVI) ? 1 : 0); fprintf(output, "SORD:\tequ %d\n", (machine == SORD) ? 1 : 0); @@ -5893,16 +7145,28 @@ int main(int argc, char *argv[]) fprintf(output, "PV2000:\tequ %d\n", (machine == PV2000) ? 1 : 0); fprintf(output, "TI99:\tequ %d\n", (machine == TI994A) ? 1 : 0); fprintf(output, "NABU:\tequ %d\n", (machine == NABU) ? 1 : 0); + fprintf(output, "SMS:\tequ %d\n", (machine == SMS) ? 1 : 0); + if (machine == NES) { + if (bank_switching) + fprintf(output, "NES_PRG_BANKS:\tequ %d\t; Each one 16K.\n", bank_rom_size / 16); + else + fprintf(output, "NES_PRG_BANKS:\tequ 2\t; Each one 16K.\n"); + fprintf(output, "NES_CHR_BANKS:\tequ %d\t; Each one 4K.\n", chrrom_size); + fprintf(output, "NES_NAMETABLE:\tequ %d\n", (nes_nametable & 1) | ((nes_nametable & 2) << 2)); + } fprintf(output, "\n"); fprintf(output, "CVBASIC_MUSIC_PLAYER:\tequ %d\n", music_used); fprintf(output, "CVBASIC_COMPRESSION:\tequ %d\n", compression_used); fprintf(output, "CVBASIC_BANK_SWITCHING:\tequ %d\n", bank_switching); + fprintf(output, "CVBASIC_BANK_ROM_SIZE:\tequ %d\n", bank_rom_size); + fprintf(output, "COLECO_SPINNER:\tequ %d\n", spinner_used); fprintf(output, "\n"); fprintf(output, "BASE_RAM:\tequ %c%04x\t; Base of RAM\n", hex, consoles[machine].base_ram - extra_ram); + fprintf(output, "RAM_SIZE:\tequ %c%04x\t; Base of RAM\n", hex, consoles[machine].memory_size + extra_ram); if ((machine == MEMOTECH || machine == EINSTEIN) && cpm_option != 0) fprintf(output, "STACK:\tequ %c%04x\t; Base stack pointer\n", hex, 0xe000); else - fprintf(output, "STACK:\tequ %C%04x\t; Base stack pointer\n", hex, consoles[machine].stack); + fprintf(output, "STACK:\tequ %c%04x\t; Base stack pointer\n", hex, consoles[machine].stack); fprintf(output, "VDP:\tequ %c%02x\t; VDP port (write)\n", hex, consoles[machine].vdp_port_write); fprintf(output, "VDPR:\tequ %c%02x\t; VDP port (read)\n", hex, consoles[machine].vdp_port_read); if (machine != TI994A) { @@ -5923,8 +7187,10 @@ int main(int argc, char *argv[]) } } strcpy(path, library_path); - if (target == CPU_6502) + if (target == CPU_6502 && machine == CREATIVISION) strcat(path, "cvbasic_6502_prologue.asm"); + else if (target == CPU_6502 && machine == NES) + strcat(path, "cvbasic_nes_prologue.asm"); else if (target == CPU_9900) strcat(path, "cvbasic_9900_prologue.asm"); else @@ -5932,7 +7198,7 @@ int main(int argc, char *argv[]) prologue = fopen(path, "r"); if (prologue == NULL) { fprintf(stderr, "Unable to open '%s'.\n", path); - exit(2); + exit(EXIT_FAILURE + 1); } while (fgets(line, sizeof(line) - 1, prologue)) { p = line; @@ -5974,7 +7240,7 @@ int main(int argc, char *argv[]) input = fopen(TEMPORARY_ASSEMBLER, "r"); if (input == NULL) { fprintf(stderr, "Unable to reopen '%s'.\n", TEMPORARY_ASSEMBLER); - exit(2); + exit(EXIT_FAILURE + 1); } while (fgets(line, sizeof(line) - 1, input)) { fputs(line, output); @@ -5983,8 +7249,10 @@ int main(int argc, char *argv[]) remove(TEMPORARY_ASSEMBLER); strcpy(path, library_path); - if (target == CPU_6502) + if (target == CPU_6502 && machine == CREATIVISION) strcat(path, "cvbasic_6502_epilogue.asm"); + else if (target == CPU_6502 && machine == NES) + strcat(path, "cvbasic_nes_epilogue.asm"); else if (target == CPU_9900) strcat(path, "cvbasic_9900_epilogue.asm"); else @@ -5992,7 +7260,7 @@ int main(int argc, char *argv[]) prologue = fopen(path, "r"); if (prologue == NULL) { fprintf(stderr, "Unable to open '%s'.\n", path); - exit(2); + exit(EXIT_FAILURE + 1); } while (fgets(line, sizeof(line) - 1, prologue)) { fputs(line, output); @@ -6002,7 +7270,37 @@ int main(int argc, char *argv[]) if (target == CPU_Z80 || target == CPU_9900) { bytes_used = process_variables(); } + + /* + ** NES CHRROM output + */ + if (machine == NES) { + if (bank_switching) { + if (chrrom_size > 4) { + fprintf(stderr, "ERROR: More than 4 CHRROM pages for bank-switched cartridge.\n"); + } + if (bank_rom_size == 256) { + fprintf(output, "\nFORG $34010\n"); + } else { + fprintf(output, "\nFORG $74010\n"); + } + fprintf(output, "\nORG $8000\n"); + } + fprintf(output, "\n; CHR data\n"); + for (c = 0; c < chrrom_size * CHRROM_PAGE; c += 8) { + fprintf(output, "\tdb $%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x\n", + chrrom_data[c], chrrom_data[c + 1], + chrrom_data[c + 2], chrrom_data[c + 3], + chrrom_data[c + 4], chrrom_data[c + 5], + chrrom_data[c + 6], chrrom_data[c + 7]); + } + free(chrrom_data); + } fclose(output); + + /* + ** Final reports + */ if (machine == MEMOTECH || machine == EINSTEIN || machine == NABU) { fprintf(stderr, "%d RAM bytes used for variables.\n", bytes_used); } else { @@ -6015,7 +7313,7 @@ int main(int argc, char *argv[]) 146; /* Support variables */ if (bytes_used > available_bytes) { fprintf(stderr, "ERROR: "); - err_code = 1; + err_code = EXIT_FAILURE; } fprintf(stderr, "%d RAM bytes used of %d bytes available.\n", bytes_used, available_bytes); } diff --git a/cvbasic.h b/cvbasic.h index b738514..8be2517 100644 --- a/cvbasic.h +++ b/cvbasic.h @@ -15,6 +15,34 @@ #define MAX_LINE_SIZE 1024 +/* + ** Supported platforms. + */ +enum supported_machine { + COLECOVISION, + SG1000, + MSX, + COLECOVISION_SGM, + SVI, + SORD, + MEMOTECH, + CREATIVISION, + PENCIL, + EINSTEIN, + PV2000, + TI994A, + NABU, + SMS, + NES, + MSX2, + TOTAL_TARGETS +}; + +extern enum supported_machine machine; + +/* + ** Supported target CPUs + */ enum cpu_target { CPU_Z80, CPU_6502, @@ -22,6 +50,28 @@ enum cpu_target { }; extern enum cpu_target target; + +/* + ** Information about supported machines + */ +struct console { + char *name; /* Machine name */ + char *options; /* Options */ + char *description; /* Description (for usage guide) */ + char *canonical; /* Canonical name */ + char *constant; /* CONST name */ + int base_ram; /* Where the RAM starts */ + int stack; /* Where the stack will start */ + int memory_size; /* Memory available */ + int vdp_port_write; /* VDP port for writing */ + int vdp_port_read; /* VDP port for reading (needed for SVI-318/328, sigh) */ + int psg_port; /* PSG port (SN76489) */ + int int_pin; /* Indicates if it uses the INT pin for video frame interrupt */ + enum cpu_target target; +}; + +extern struct console consoles[TOTAL_TARGETS]; + extern char temp[MAX_LINE_SIZE]; extern int optimized; extern FILE *output; @@ -49,16 +99,16 @@ struct label { /* ** These flags are used in labels */ -#define LABEL_USED 0x10 -#define LABEL_DEFINED 0x20 +#define LABEL_USED 0x10 +#define LABEL_DEFINED 0x20 #define LABEL_CALLED_BY_GOTO 0x40 #define LABEL_CALLED_BY_GOSUB 0x80 -#define LABEL_IS_PROCEDURE 0x100 -#define LABEL_IS_VARIABLE 0x200 -#define LABEL_IS_ARRAY 0x400 +#define LABEL_IS_PROCEDURE 0x100 +#define LABEL_IS_VARIABLE 0x200 +#define LABEL_IS_ARRAY 0x400 -#define LABEL_VAR_READ 0x0800 -#define LABEL_VAR_WRITE 0x1000 -#define LABEL_VAR_ACCESS (LABEL_VAR_READ | LABEL_VAR_WRITE) +#define LABEL_VAR_READ 0x0800 +#define LABEL_VAR_WRITE 0x1000 +#define LABEL_VAR_ACCESS (LABEL_VAR_READ | LABEL_VAR_WRITE) extern void emit_error(char *); diff --git a/cvbasic_6502_prologue.asm b/cvbasic_6502_prologue.asm index a3ef3ee..f791141 100644 --- a/cvbasic_6502_prologue.asm +++ b/cvbasic_6502_prologue.asm @@ -14,6 +14,11 @@ ; with flicker flag). ; Revision date: Oct/15/2024. Added LDIRMV. ; Revision date: Nov/12/2024. Saves the VDP status. + ; Revision date: Mar/25/2026. Faster _mul16 (using Y in loop) and _div16 + ; (avoids update using registers) + ; Revision date: Mar/29/2026. Avoids unwinding stack for _mul16, _div16, _div16s, + ; _mod16, and _mod16s. Optimized _mul16 in two + ; loops (28% speed-up) ; CPU 6502 @@ -451,14 +456,16 @@ print_digit: .4: DEX BEQ .6 LDX temp+1 - BNE .5 + BNE print_char .6: LDX #$30 .3: PHA LDA #1 STA temp PLA -.5: PHA + +print_char: + PHA TYA PHA LDA cursor+1 @@ -664,55 +671,46 @@ _peek16: ; 16-bit multiplication. _mul16: - PLA - STA result - PLA - STA result+1 - PLA - STA temp2+1 - PLA STA temp2 - LDA result+1 - PHA - LDA result - PHA + STY temp2+1 LDA #0 STA result - STA result+1 - LDX #15 + TAY + ; Shift low-byte of multiplier + LDX #7 .1: - LSR temp2+1 - ROR temp2 + LSR temp2 BCC .2 LDA result CLC ADC temp STA result - LDA result+1 + TYA ADC temp+1 - STA result+1 + TAY .2: ASL temp ROL temp+1 DEX BPL .1 + ; Shift high-byte of multiplier (temp is zero here) + TYA + LDX #7 +.3: + LSR temp2+1 + BCC .4 + CLC + ADC temp+1 +.4: ASL temp+1 + DEX + BPL .3 + TAY LDA result - LDY result+1 RTS ; 16-bit signed modulo. _mod16s: - PLA - STA result - PLA - STA result+1 - PLA - STA temp2+1 - PLA STA temp2 - LDA result+1 - PHA - LDA result - PHA + STY temp2+1 LDY temp2+1 PHP BPL .1 @@ -737,18 +735,8 @@ _mod16s: ; 16-bit signed division. _div16s: - PLA - STA result - PLA - STA result+1 - PLA - STA temp2+1 - PLA STA temp2 - LDA result+1 - PHA - LDA result - PHA + STY temp2+1 LDA temp+1 EOR temp2+1 PHP @@ -774,18 +762,8 @@ _div16s: RTS _div16: - PLA - STA result - PLA - STA result+1 - PLA - STA temp2+1 - PLA STA temp2 - LDA result+1 - PHA - LDA result - PHA + STY temp2+1 .1: LDA #0 STA result @@ -799,20 +777,13 @@ _div16: LDA result SEC SBC temp - STA result + TAY LDA result+1 SBC temp+1 + BCC .3 + STY result STA result+1 - BCS .3 - LDA result - ADC temp - STA result - LDA result+1 - ADC temp+1 - STA result+1 - CLC -.3: - DEX +.3: DEX BPL .2 ROL temp2 ROL temp2+1 @@ -821,18 +792,8 @@ _div16: RTS _mod16: - PLA - STA result - PLA - STA result+1 - PLA - STA temp2+1 - PLA STA temp2 - LDA result+1 - PHA - LDA result - PHA + STY temp2+1 .1: LDA #0 STA result diff --git a/cvbasic_9900_prologue.asm b/cvbasic_9900_prologue.asm index c59b78e..1059992 100644 --- a/cvbasic_9900_prologue.asm +++ b/cvbasic_9900_prologue.asm @@ -18,6 +18,7 @@ ; Revision date Aug/18/2024. Ported bugfixes to TMS9900 version ; Revision date Aug/30/2024. All samples except pletter and banking working on TMS9900 version ; Revision date Oct/15/2024. Added LDIRMV. +; Revision date May/03/2025. Fix for unpack3 which was using the stack pointer as a temp register ; ; Platforms supported: @@ -134,7 +135,7 @@ lastsp equ $ ; reads VDP status for us (no choice). intcnt equ >8379 ; interrupt counter byte, adds 1 (from GPLWS r14) every frame -vdp_status equ >837B ; VDP status byte mirror +vdp_status equ >837B ; VDP status byte mirror intwsr1 equ >83c2 ; INT WS R1 - interrupt control flags - must be >8000 intwsr2 equ >83c4 ; INT WS R2 - address of user interrupt routine (point to int_handler) intwsr11 equ >83d6 ; screen timeout counter - must be odd (init to 1, is inct every frame) @@ -256,14 +257,14 @@ FILVRM ; Read VRAM - address in R0, CPU data at R2, count in R3 ; Inline address set to avoid needing to cache r11 LDIRMV - swpb r2 - movb r2,@VDPWADR - swpb r2 - movb r2,@VDPWADR - swpb r2 - swpb r2 + swpb r0 + movb r0,@VDPWADR + swpb r0 + movb r0,@VDPWADR + swpb r0 + swpb r0 !1 - movb @VDPDATA,*r0+ + movb @VDPDATA,*r2+ dec r3 jne -!1 b *r11 @@ -324,16 +325,15 @@ ENASCR ; address in R8, CPU data at R9, count per row in R6 (MSB), number rows in R4 (MSB), CPU stride in R5 (MSB) (VDP stride fixed at 32) ; original: address in pointer, CPU address at temp, count per row in temp2, num rows in temp2+1, stride in YYXX CPYBLK - limi 0 mov r11,r7 ; save return srl r6,8 srl r4,8 srl r5,8 ; bytes to words + limi 0 !1 mov r8,r0 ; get vdp address mov r9,r2 ; get cpu address mov r6,r3 ; get count -!2 bl @LDIRVM ; copy one row a r5,r9 ; add stride to CPU address ai r8,32 ; add 32 to VDP @@ -416,13 +416,13 @@ print_digit ci r5,>0200 jl !6 mov r5,r2 - jne !5 + jne print_char2 !6 li r2,>30 !3 andi r5,>00ff ori r5,>0100 -!5 +print_char2 mov @cursor,r0 ; get cursor andi r0,>07ff ; enforce position - large range for two screen pages ai r0,>1800 ; add is safer than OR, and we have that option @@ -431,6 +431,9 @@ print_digit movb r2,@VDPWDATA inc @cursor ; track it b *r6 +print_char + mov r11,r6 + b @print_char2 ; Load sprite definitions: Sprite number in R4, CPU data in R0, count of sprites in R5 (MSB) ; Original: pointer = sprite number, temp = CPU address, a = number sprites @@ -602,7 +605,7 @@ rmask data >b400 ; mask for 16 bit .endif -; Set SN Frequency: R0=freqency code, R2=channel command (MSB) +; Set SN Frequency: R0=frequency code, R2=channel command (MSB) ; Original: A=least significant byte X=channel command Y=most significant byte sn76489_freq mov r0,r3 @@ -1501,6 +1504,7 @@ define_char_unpack movb @mode,r0 ; get mode andi r0,>0800 ; check bitmap bit jeq unpack3 ; 3 times if yes + mov r4,r1 jmp unpack ; once if no ; Load bitmap color definitions: Char number in R4, CPU data in R0, count in R5 (MSB) @@ -1509,22 +1513,22 @@ define_color_unpack mov r0,r2 andi r4,>00ff ; mask off to 0-255 sla r4,3 ; char times 8 - li r0,>4000 ; base of color table - a r0,r4 ; set base for color then fall through + ai r4,>2000 ; base of color table ; entered from one of the above two functions unpack3 mov r11,r9 ; save return address - mov r4,r10 ; save VDP address - mov r2,r4 + mov r4,r15 ; save VDP address + mov r2,r14 ; save CPU address + mov r15,r1 bl @unpack - ai r10,>800 - mov r10,r1 - mov r4,r2 + ai r15,>800 + mov r15,r1 + mov r14,r2 bl @unpack - ai r10,>800 - mov r10,r1 - mov r4,r2 + ai r15,>800 + mov r15,r1 + mov r14,r2 bl @unpack b *r9 @@ -1534,15 +1538,6 @@ unpack3 ; Unpack data to VDP: VDP address in R1, CPU data in R2 ; Original: pointer = VDP, temp = CPU address, a = number chars -; The challenge with porting is the 8 bit vs 16-bit registers, and the behaviour of rotate -; 9900 shifts and rotates are always 16 bits wide, and do not include the carry bit -; rather, shifted out bits are /copied/ to carry, but carry is never copied in. This makes -; most of the optimized code difficult to port - but if we knew the original intent, it -; wouldn't be so bad. In addition, the 9900 has very few instructions that preserve -; status flags, meaning we can't execute an instruction and then check status for the -; instruction before it in nearly any case, the code uses this too. -; I've not been able to locate any C code for the unpacker which would probably be easier to port. - r0lsb equ mywp+1 r1lsb equ mywp+3 r2lsb equ mywp+5 diff --git a/cvbasic_epilogue.asm b/cvbasic_epilogue.asm index 775864e..e13f8e0 100644 --- a/cvbasic_epilogue.asm +++ b/cvbasic_epilogue.asm @@ -19,10 +19,19 @@ ; Revision date: Aug/02/2024. Added rom_end label for Memotech. ; Revision date: Aug/15/2024. Added support for Tatung Einstein. ; Revision date: Nov/12/2024. Added vdp_status. + ; Revision date: Feb/03/2025. Round final ROM size to 8K multiples. + ; Revision date: Feb/05/2026. Added support for spinners and roller controller + ; (Colecovision). + ; Revision date: Jul/14/2026. Added program_fm. + ; Revision date: Jul/20/2026. Moved program_fm to cvbasic_prologue.asm. ; rom_end: + ; ROM final size rounding + if MSX+COLECO+SG1000+SMS+SVI+SORD + TIMES (($+$1FFF)&$1e000)-$ DB $ff + endif if MEMOTECH+EINSTEIN+NABU ; Align following data to a 256-byte page. TIMES $100-($&$ff) DB $4f @@ -30,13 +39,538 @@ rom_end: if PV2000 TIMES $10000-$ DB $ff endif - if COLECO+SG1000+MSX+SVI+SORD+PV2000 + if SG1000+SMS + if CVBASIC_BANK_SWITCHING + forg CVBASIC_BANK_ROM_SIZE*1024-1 ; Force final ROM size + db $ff + endif + forg $7FF0 + org $7FF0 + db "TMR SEGA" + db 0,0 + db 0,0 ; Checksum + db $11,$78 ; Product code + db $00 ; Version + db $4c ; SMS Export + 32KB for checksum + endif + + if MSX + if FM_SUPPORT + ; + ; FM driver for MSX + ; + ; by Oscar Toledo G. (nanochess) + ; + ; Creation date: Sep/28/2012. For Mecha-9. + ; Revision date: Mar/07/2026. Adapted for Metro Wars (CVBasic) + ; Revision date: Mar/12/2026. Adapted for release in CVBasic. + ; + + FORG $01FD00 + ORG $BD00 + + ; Subroutines from BIOS MSX +CALSLT: equ $001c ; Inter-slot call (disables interruptions) +;ENASLT: equ $0024 ; Enable slot (H=High-byte address, C=Slot) + + ; Subroutines from OPLL ROM +WRTOPL: equ $4110 +INIOPL: equ $4113 + + ; + ; Detects FM (OPLL) + ; Carry = Set = FM detected. + ; Clear = No FM detected. + ; +msx_detect_fm: + ld bc,$0000 +.1: push bc + ld hl,$fcc1 ; EXPTBL + ld a,l + add a,c + ld l,a + ld a,(hl) + and $80 + or c + call msx_find_fm ; Search in subslots. + pop bc + ret nc ; Jump if something has been found. + inc c + bit 2,c ; Four slots analyzed? + jr z,.1 ; No, jump. + ret + +msx_fm_signature_1: db "APRLOPLL" ; Internal MSX-Music. +msx_fm_signature_2: db "PAC2OPLL" ; FM-Pac cartridge. + +msx_find_fm: + bit 7,a ; Expanded slot? + ld b,1 ; No subslots. + jr z,.1 ; No, jump. + and $f3 + ld b,4 ; Ok, four subslots. +.1: ld c,a +.2: push bc + ld h,$40 + ld a,c + call ENASLT + ld hl,$4018 + push hl + ld de,msx_fm_signature_1 + ld b,8 +.4: ld a,(de) + cp (hl) + jr nz,.5 + inc de + inc hl + djnz .4 +.5: pop hl + jr z,.8 ; Detected? Jump to take note. + ld de,msx_fm_signature_2 + ld b,8 +.6: ld a,(de) + cp (hl) + jr nz,.7 + inc de + inc hl + djnz .6 +.7: jr nz,.3 ; Not detected? Jump to next subslot. +.8: pop bc + ld a,c ; Take note of slot. + ld (fm_slot),a + push bc + ld a,1 ; Enable FM. + ld (fm_enabled),a +.3: + ; Important! The H register should be $40 here. + ld a,(cartridge_slot) + call ENASLT + pop bc + ld a,(fm_enabled) + or a + ret nz + ld a,c + add a,4 + ld c,a + djnz .2 + scf + ret + + ; Write a FM register + ; A = register + ; E = data +write_fm_zero: + ld e,0 +write_fm: + jp WRTOPL + + ; + ; Map FM BIOS 16K in $4000-$7FFF + ; +fm_rom_switch: + di + ld a,(fm_slot) + ld h,$40 + jp ENASLT + + ; + ; Map cartridge in $4000-$7FFF + ; +cartridge_rom_switch: + ld a,(cartridge_slot) + ld h,$40 + call ENASLT + ei + ret + + endif + endif + + if SMS + if FM_SUPPORT + ; + ; FM driver for Sega Master System. + ; + ; by Oscar Toledo G. (nanochess) + ; + ; Creation date: Sep/28/2012. For MSX. + ; Revision date: Aug/16/2013. Adapted to Sega Master System. + ; Revision date: Mar/12/2026. Adapted to CVBasic. + FORG $01FD00 + ORG $7D00 + + ; + ; Detect FM (OPLL) + ; +sms_detect_fm: + ld a,($c000) + or $04 + out ($3e),a + ld bc,$0700 +.1: ld a,b + and $01 + out ($f2),a + ld e,a + in a,($f2) + and $07 + cp e + jr nz,.2 + inc c +.2: djnz .1 + ld a,c + cp $07 + jr z,.3 + xor a +.3: and 1 + out ($f2),a + ld (fm_enabled),a + dec a ; For compatibility. + ld (fm_slot),a + ld a,($c000) + out ($3e),a + ret + + ; Write a FM register + ; A = register + ; E = data +write_fm: + out ($f0),a + ld a,e + out ($f1),a + ex (sp),hl + ex (sp),hl + ret + +fm_rom_switch: + di + ret + +cartridge_rom_switch: + ei + ret + + endif + endif + + if MSX+SMS + if FM_SUPPORT + ; + ; This code cannot be in the area $4000-$7fff + ; + + ; + ; Play music for FM + ; +music_generate_fm: + ; Fragments of code in caller. + +.0: ld a,(music_timing) + and $3f ; Restarts note time. + ld (music_note_counter),a + push hl + push bc + push de + call fm_rom_switch + ld a,(fm_new) + or a ; New programmed instrument. + jr z,.8 + xor a + ld (fm_new),a + ld hl,fm_data + xor a +.10: push af + ld e,(hl) + call write_fm + inc hl + pop af + inc a + cp $08 + jr nz,.10 +.8: + pop de + pop bc + ld a,b + cp $3f ; Sustain? + jr z,.1 + ld a,$10 + call play_fm +.1: ld a,c ; Read second voice. + cp $3f ; Sustain? + jr z,.2 + ld b,a + ld a,$11 + call play_fm +.2: ld a,d ; Read third voice. + cp $3f ; Sustain? + jr z,.3 + ld b,a + ld a,$12 + call play_fm +.3: push de + call cartridge_rom_switch + pop de + pop hl + ld a,e ; Read effect. + ld (music_drum),a + xor a + ld (music_counter_4),a + inc hl + inc hl + inc hl + ld a,(music_timing) + and $c0 + jr nz,.14 + inc hl +.14: ld (music_pointer),hl + ; + ; Build extras + ; +.6: ld a,(music_drum) ; Read effect. + or a ; Effect happening? + jr z,.7 ; No, jump. + call fm_rom_switch + ld a,(music_drum) ; Read effect. + dec a ; 1 - Long drum. + jr nz,.5 + ld a,(music_counter_4) + or a + ld de,$0e28 + jr z,.9 + cp 3 + ld de,$0e20 + jr z,.9 + jr .4 + +.5: dec a ; 2 - Short drum. + jr nz,.11 + ld a,(music_counter_4) + or a + ld e,$21 + jr z,.9 + cp 3 + ld e,$20 + jr nz,.4 +.9: ld a,$0e + call write_fm + jr .4 + +.12: cp 2 + jr nz,.9 + ld e,$20 + ld a,$0e + call write_fm + ld e,$28 + jr .9 + +.11: ;dec a ; 3 - Roll. + ;jp nz,.4 + ld a,(music_timing) + and $3e + rrca + ld b,a + ld a,(music_counter_4) + ld e,$28 + or a + jr z,.9 + cp b + jr z,.12 + ld e,$20 + cp 2 + jr z,.9 + dec a + dec a + cp b + jr z,.9 +.4: call cartridge_rom_switch +.7: + ; Increment time for drum. + ld hl,music_counter_4 + inc (hl) + ld hl,music_note_counter + dec (hl) + ret + + ; + ; Play note in FM + ; A = Voice ($10 - $11 - $12) + ; B = Instrument + Note. + ; +play_fm: + push bc + push de + ld c,a ; Saves base register. + add a,$10 + push bc + call write_fm_zero ; Turn off voice if it was turned on. + pop bc + ld a,b ; Read instrument (bits 7-6) + and $c0 + rlca + rlca + ld hl,fm_inst + add a,l + ld l,a + adc a,h + sub l + ld h,a + ld e,(hl) ; Select instrument. + + ld a,c + add a,$20 + push bc + call write_fm + pop bc + ld a,b ; Read note. + and $3f + ld hl,fm_notes + add a,a ; Index into note table. + add a,l + ld l,a + adc a,h + sub l + ld h,a + ld a,c + ld e,(hl) ; Get low byte. + push bc + push hl + call write_fm + pop hl + pop bc + inc hl + ld e,(hl) ; Get high byte (octave). + ld a,b + cpl + and $c0 ; Is it bass? + jr nz,.1 ; No, jump. + ld a,e + sub 2 ; Lower octave. + ld e,a +.1: ld a,b + and $c0 + cp $40 ; Is it clarinet? + jr nz,$+4 ; No, jump. + inc e ; Higher octave. + inc e + ld a,c + add a,$10 + call write_fm + pop de + pop bc + ret + + ; + ; Init FM + ; +init_fm: + push ix + push iy + call fm_rom_switch + if MSX + ld hl,$ef00 + call INIOPL + endif + ld hl,.0 + ld b,24 +.1: ld e,(hl) + inc hl + ld a,(hl) + inc hl + call write_fm + djnz .1 + call cartridge_rom_switch + ld hl,.2 + ld de,fm_inst + ld bc,4 + ldir + pop iy + pop ix + ret + +.0: dw $0011 ; Unique programmable instrument (not used) + dw $0111 + dw $0220 + dw $0320 + dw $04ff + dw $05b2 + dw $06f4 + dw $07f4 + dw $0e20 ; Enables percussion mode. + dw $2000 ; Turn off main voices. + dw $2100 + dw $2200 + dw $2300 + dw $2400 + dw $2500 + dw $1620 ; Set up percussion. + dw $1750 + dw $18c0 + dw $2605 + dw $2705 + dw $2801 + dw $3600 ; / Bass Drum (volume for percussion) + dw $3720 ; Hihat / Snare Drum + dw $3800 ; Tom / Top Cymbal + +.2: + db $30 ; Piano. + db $50 ; Clarinet. + db $40 ; Flute. + db $e0 ; Clarinet bass. + + ; + ; Turn off FM + ; +turn_off_fm: + ld a,(fm_slot) + inc a + ret z + ld a,(fm_enabled) + or a + ret z + call fm_rom_switch + LD A,$10+$10 + CALL write_fm_zero + LD A,$11+$10 + CALL write_fm_zero + LD A,$12+$10 + CALL write_fm_zero + CALL cartridge_rom_switch + RET + +fm_notes: + ; Silence - 0 + dw 0 + ; Octave 2 - 1 + dw $14ad,$14b7,$14c2,$14cd,$14d9,$14e6 + dw $14f4,$1503,$1512,$1522,$1534,$1546 + ; Octave 3 - 13 + dw $16ad,$16b7,$16c2,$16cd,$16d9,$16e6 + dw $16f4,$1703,$1712,$1722,$1734,$1746 + ; Octave 4 - 25 + dw $18ad,$18b7,$18c2,$18cd,$18d9,$18e6 + dw $18f4,$1903,$1912,$1922,$1934,$1946 + ; Octave 5 - 37 + dw $1aad,$1ab7,$1ac2,$1acd,$1ad9,$1ae6 + dw $1af4,$1b03,$1b12,$1b22,$1b34,$1b46 + ; Octave 6 - 49 + dw $1cad,$1cb7,$1cc2,$1ccd,$1cd9,$1ce6 + dw $1cf4,$1d03,$1d12,$1d22,$1d34,$1d46 + ; Octave 7 - 61 + ; Space for two notes more. + endif + endif + + if COLECO+SG1000+SMS+MSX+SVI+SORD+PV2000 org BASE_RAM endif ram_start: sprites: + if SMS + rb 256 + else rb 128 + endif sprite_data: rb 4 frame: @@ -47,8 +581,37 @@ cursor: rb 2 lfsr: rb 2 -mode: + if MSX+SMS +cartridge_slot: + rb 1 +fm_slot: rb 1 +fm_enabled: + rb 1 +fm_inst: + rb 4 +fm_new: + rb 1 +fm_data: + rb 8 + endif +mode: + rb 1 ; bit 0: NMI disabled. + ; bit 1: NMI received. + ; bit 2: No sprite flicker. + ; bit 3: Single charset mode. + ; bit 4: MSX2 sprites. + ; bit 5: MSX2 extended modes (MODE 5-8) + if MSX&2 +cls_size: + rb 2 ; Screen length for CLS +msx2_mode: + rb 1 ; Type of color for mode. +msx2_fg_color: + rb 1 ; Current foreground color. +msx2_bk_color: + rb 1 ; Current background color. + endif flicker: rb 1 joy1_data: @@ -63,6 +626,12 @@ ntsc: rb 1 vdp_status: rb 1 + if COLECO + if COLECO_SPINNER +spinner_data: + rb 3 + endif + endif if NABU nabu_data0: rb 1 nabu_data1: rb 1 diff --git a/cvbasic_nes_epilogue.asm b/cvbasic_nes_epilogue.asm new file mode 100644 index 0000000..7f8ded4 --- /dev/null +++ b/cvbasic_nes_epilogue.asm @@ -0,0 +1,10 @@ +rom_end: + if CVBASIC_BANK_SWITCHING + forg CVBASIC_BANK_ROM_SIZE*1024+16-6 ; Go to final of ROM minus vectors + else + times $fffa-$ db $ff + endif + + dw nmi_handler + dw START + dw irq_handler diff --git a/cvbasic_nes_prologue.asm b/cvbasic_nes_prologue.asm new file mode 100644 index 0000000..108fc39 --- /dev/null +++ b/cvbasic_nes_prologue.asm @@ -0,0 +1,1681 @@ + ; + ; CVBasic prologue (BASIC compiler, 6502 target) + ; + ; by Oscar Toledo G. + ; https://nanochess.org/ + ; + ; Creation date: May/13/2025. + ; Revision date: Jul/20/2025. Added code to load sprites and read controllers. + ; Revision date: Aug/23/2025. Support for writing VRAM and PRINT. + ; Revision date: Aug/24/2025. Support for scrolling, SCREEN, and DISABLE/ENABLE. + ; Added music player. + ; Revision date: Aug/25/2025. Added support for 256K and 512K ROM. + ; Revision date: Aug/26/2025. Added support for CHRRAM selection. + ; Revision date: Feb/16/2026. Relocated PPUBUF. + ; Revision date: Mar/25/2026. Faster _mul16 (using Y in loop) and _div16 + ; (avoids update using registers) + ; Revision date: Mar/29/2026. Avoids unwinding stack for _mul16, _div16, _div16s, + ; _mod16, and _mod16s. + ; + + CPU 6502 + + ; + ; Platforms supported: + ; o NES/Famicom + ; + + ; + ; CVBasic variables in zero page. + ; + +ppu_source: equ $00 ; Used in NMI for source address for PPU copy + ; This is a block of 8 bytes that should stay together. +temp: equ $02 +temp2: equ $04 +result: equ $06 +pointer: equ $08 + +read_pointer: equ $0a +cursor: equ $0c +mode: equ $0e +ntsc: equ $0f + +vdp_status: equ $10 +flicker: equ $11 +frame: equ $12 +ppu_pointer: equ $14 +ppu_temp: equ $15 ; Used in NMI to save X +ppu_ctrl: equ $16 +ppu_mask: equ $17 +scroll_x: equ $18 +scroll_y: equ $1a +CHRRAM_BANK: equ $1c +cont_bits: equ $1d +lfsr: equ $1e + +joy1_data: equ $20 +joy2_data: equ $21 +key1_data: equ $22 +key2_data: equ $23 +sprite_data: equ $24 + + IF CVBASIC_MUSIC_PLAYER +music_playing: EQU $4f +music_bank: EQU $30 +music_timing: EQU $31 +music_start: EQU $32 +music_pointer: EQU $34 +music_note_counter: EQU $36 +music_instrument_1: EQU $37 +music_note_1: EQU $38 +music_counter_1: EQU $39 +music_instrument_2: EQU $3a +music_note_2: EQU $3b +music_counter_2: EQU $3c +music_instrument_3: EQU $3d +music_note_3: EQU $3e +music_counter_3: EQU $3f +music_drum: EQU $40 +music_counter_4: EQU $41 +audio_freq1: EQU $42 +audio_freq2: EQU $44 +audio_freq3: EQU $46 +audio_vol1: EQU $48 +audio_vol2: EQU $49 +audio_vol3: EQU $4a +audio_vol4hw: EQU $4b +audio_noise: EQU $4c +music_tick: EQU $4d +music_mode: EQU $4e + ENDIF + +SPRITE_PAGE: EQU $02 +PPUBUF: EQU $0140 +PPUSIZE: EQU $40 +BANKSEL: EQU $C000 + + FORG $0000 + ; The ORG address doesn't matter here + ; iNES cartridge header + DB "NES",$1A + DB NES_PRG_BANKS + if CVBASIC_BANK_SWITCHING + DB 0 ; It has CHRRAM + DB $e2|NES_NAMETABLE ; Cartridge type LSB + DB $10 ; Cartridge type MSB + DB $00 ; Number of 8K RAM pages. + else + DB NES_CHR_BANKS + DB $00|NES_NAMETABLE ; Cartridge type LSB + DB $00 ; Cartridge type MSB + DB $00 ; Number of 8K RAM pages. + endif + DB $00,$00,$00,$00,$00,$00,$00 ; Reserved + + if CVBASIC_BANK_SWITCHING + if CVBASIC_BANK_ROM_SIZE-512 + FORG $3c010 + ORG $c000 + else + FORG $7c010 + ORG $c000 + endif + else + FORG $0010 + ORG $8000 + endif + +PPUCTRL: EQU $2000 +PPUMASK: EQU $2001 +PPUSTATUS: EQU $2002 +OAMADDR: EQU $2003 +OAMDATA: EQU $2004 +PPUSCROLL: EQU $2005 +PPUADDR: EQU $2006 +PPUDATA: EQU $2007 +SPRRAM: EQU $4014 +CONT1: EQU $4016 +CONT2: EQU $4017 + + ; + ; NES architecture prevents direct access to VRAM except + ; during the VBLANK. + ; +WRTVRM: + PHA + TXA + LDX ppu_pointer + STA PPUBUF+2,X + PLA + STA PPUBUF,X + TYA + ORA #$40 + STA PPUBUF+1,X + INX + INX + INX + STX ppu_pointer + CPX #PPUSIZE + BCS .1 + RTS +.1: + JMP wait + +LDIRVM4: + JSR LDIRVM2 + JSR LDIRVM2 + JSR LDIRVM2 + JSR LDIRVM2 + RTS + +LDIRVM2: + JSR LDIRVM + LDA pointer + CLC + ADC temp2 + STA pointer + BCC .1 + INC pointer+1 +.1: + RTS + +LDIRVM: + LDX ppu_pointer + LDA pointer + STA PPUBUF,X + LDA pointer+1 + STA PPUBUF+1,X + LDA #0 + SEC + SBC temp2 + STA PPUBUF+2,X + LDA temp + SEC + SBC PPUBUF+2,X + STA PPUBUF+3,X + LDA temp+1 + SBC #0 + STA PPUBUF+4,X + TXA + CLC + ADC #5 + STA ppu_pointer + CMP #PPUSIZE + BCS .1 + RTS +.1: + JMP wait + +ENASCR: + LDA ppu_mask + ORA #$18 + STA ppu_mask + RTS + +DISSCR: + LDA ppu_mask + AND #$E7 + STA ppu_mask + RTS + +CPYBLK: +.1: + LDA temp2 + PHA + LDA temp2+1 + PHA + TXA + PHA + TYA + PHA + LDA temp + PHA + LDA temp+1 + PHA + LDA #0 + STA temp2+1 + JSR LDIRVM + PLA + STA temp+1 + PLA + STA temp + PLA + STA temp2+1 + PLA + STA temp2 + LDA temp + CLC + ADC temp2 + STA temp + LDA temp+1 + ADC temp2+1 + STA temp+1 + LDX temp2 + LDY temp2+1 + PLA + STA temp2+1 + PLA + STA temp2 + LDA pointer + CLC + ADC #$20 ; !!! Variation for scrolling + STA pointer + LDA pointer+1 + ADC #$00 + STA pointer+1 + DEC temp2+1 + BNE .1 + RTS + +cls: + LDA #$80 +.1: + PHA + LDX ppu_pointer + CMP #$8F + BNE .2 + LDA #$00 + BEQ .3 + +.2: LDA #$20 +.3: STA PPUBUF+3,X + LDA #$40 + STA PPUBUF+2,X + LDA #$00 + STA PPUBUF,X + PLA + STA PPUBUF+1,X + PHA + CLC + ROR PPUBUF+1,X + ROR PPUBUF,X + SEC + ROR PPUBUF+1,X + ROR PPUBUF,X + INX + INX + INX + INX + STX ppu_pointer + JSR wait + PLA + CLC + ADC #1 + CMP #$90 + BNE .1 + RTS + +update_sprite: + ASL A + ASL A + STA pointer + LDA #SPRITE_PAGE + STA pointer+1 + LDY #0 + LDA sprite_data+0 + STA (pointer),Y + INY + LDA sprite_data+1 + STA (pointer),Y + INY + LDA sprite_data+2 + STA (pointer),Y + INY + LDA sprite_data+3 + STA (pointer),Y + RTS + +_abs16: + PHA + TYA + BPL _neg16.1 + PLA +_neg16: + EOR #$FF + CLC + ADC #1 + PHA + TYA + EOR #$FF + ADC #0 + TAY +.1: + PLA + RTS + +_sgn16: + STY temp + ORA temp + BEQ .1 + TYA + BMI .2 + LDA #0 + TAY + LDA #1 + RTS + +.2: LDA #$FF +.1: TAY + RTS + +_read16: + JSR _read8 + PHA + JSR _read8 + TAY + PLA + RTS + +_read8: + LDY #0 + LDA (read_pointer),Y + INC read_pointer + BNE .1 + INC read_pointer+1 +.1: + RTS + +_peek8: + STA pointer + STY pointer+1 + LDY #0 + LDA (pointer),Y + RTS + +_peek16: + STA pointer + STY pointer+1 + LDY #0 + LDA (pointer),Y + PHA + INY + LDA (pointer),Y + TAY + PLA + RTS + + ; temp2 contains left side (dividend) + ; temp contains right side (divisor) + + ; 16-bit multiplication. +_mul16: + STA temp2 + STY temp2+1 + LDA #0 + STA result + TAY + ; Shift low-byte of multiplier + LDX #7 +.1: + LSR temp2 + BCC .2 + LDA result + CLC + ADC temp + STA result + TYA + ADC temp+1 + TAY +.2: ASL temp + ROL temp+1 + DEX + BPL .1 + ; Shift high-byte of multiplier (temp is zero here) + TYA + LDX #7 +.3: + LSR temp2+1 + BCC .4 + CLC + ADC temp+1 +.4: ASL temp+1 + DEX + BPL .3 + TAY + LDA result + RTS + + ; 16-bit signed modulo. +_mod16s: + STA temp2 + STY temp2+1 + LDY temp2+1 + PHP + BPL .1 + LDA temp2 + JSR _neg16 + STA temp2 + STY temp2+1 +.1: + LDY temp+1 + BPL .2 + LDA temp + JSR _neg16 + STA temp + STY temp+1 +.2: + JSR _mod16.1 + PLP + BPL .3 + JMP _neg16 +.3: + RTS + + ; 16-bit signed division. +_div16s: + STA temp2 + STY temp2+1 + LDA temp+1 + EOR temp2+1 + PHP + LDY temp2+1 + BPL .1 + LDA temp2 + JSR _neg16 + STA temp2 + STY temp2+1 +.1: + LDY temp+1 + BPL .2 + LDA temp + JSR _neg16 + STA temp + STY temp+1 +.2: + JSR _div16.1 + PLP + BPL .3 + JMP _neg16 +.3: + RTS + +_div16: + STA temp2 + STY temp2+1 +.1: + LDA #0 + STA result + STA result+1 + LDX #15 +.2: + ROL temp2 + ROL temp2+1 + ROL result + ROL result+1 + LDA result + SEC + SBC temp + TAY + LDA result+1 + SBC temp+1 + BCC .3 + STY result + STA result+1 +.3: DEX + BPL .2 + ROL temp2 + ROL temp2+1 + LDA temp2 + LDY temp2+1 + RTS + +_mod16: + STA temp2 + STY temp2+1 +.1: + LDA #0 + STA result + STA result+1 + LDX #15 +.2: + ROL temp2 + ROL temp2+1 + ROL result + ROL result+1 + LDA result + SEC + SBC temp + STA result + LDA result+1 + SBC temp+1 + STA result+1 + BCS .3 + LDA result + ADC temp + STA result + LDA result+1 + ADC temp+1 + STA result+1 + CLC +.3: + DEX + BPL .2 + LDA result + LDY result+1 + RTS + + ; Random number generator. + ; From my game Mecha Eight. +random: + LDA lfsr + ORA lfsr+1 + BNE .0 + LDA #$11 + STA lfsr + LDA #$78 + STA lfsr+1 +.0: LDA lfsr+1 + ROR A + ROR A + ROR A + EOR lfsr+1 + STA temp + LDA lfsr+1 + ROR A + ROR A + EOR temp + STA temp + LDA lfsr + ASL A + ASL A + EOR temp + ROL A + ROR lfsr+1 + ROR lfsr + LDA lfsr + LDY lfsr+1 + RTS + +irq_handler: + RTI + +nmi_handler: + PHA + TXA + PHA + TYA + PHA + + if CVBASIC_BANK_SWITCHING + LDA $BFFF + PHA + endif + ; Load sprites + LDA mode + AND #4 ; Flicker enabled? + BNE .5 ; No, jump. + LDA flicker + CLC + ADC #32 + STA flicker + JMP .6 +.5: + LDA #$00 +.6: + STA OAMADDR + LDX #SPRITE_PAGE + STX SPRRAM ; Use DMA for sprite loading + + ; Screen changes + LDA ppu_pointer ; Any change? + BEQ .1 ; No, jump. + LDX #$00 +.0: LDY PPUBUF,X + INX + LDA PPUBUF,X + INX + STA PPUADDR ; High-byte of VRAM address. + ROL A + STY PPUADDR ; Low-byte of VRAM address. + BCS .2 ; Fill routine. + BMI .7 ; Single byte routine. + ; Copy routine. + LDA PPUBUF+1,X + STA ppu_source + LDA PPUBUF+2,X + STA ppu_source+1 + LDY PPUBUF,X ; Negative counter. + INX + INX + INX +.4: + LDA (ppu_source),Y + STA PPUDATA + INY + BNE .4 + CPX ppu_pointer + BNE .0 + JMP .11 + + ; Single byte +.7: + LDA PPUBUF,X + INX + STA PPUDATA + CPX ppu_pointer + BNE .0 + JMP .11 + + ; Filling data +.2: + LDY PPUBUF,X + INX + LDA PPUBUF,X + INX +.3: + STA PPUDATA + DEY + BNE .3 + CPX ppu_pointer + BNE .0 + +.11: + LDA #0 + STA ppu_pointer +.1: + + ; Final settings for PPU + LDA #0 + STA PPUADDR + STA PPUADDR + LDA scroll_x + STA PPUSCROLL + LDA scroll_y + STA PPUSCROLL + LDA ppu_ctrl + LSR A + LSR A + STA ppu_temp + LDA scroll_y+1 + ROR A + ROL ppu_temp + LDA scroll_x+1 + ROR A + ROL ppu_temp + LDA ppu_temp + STA PPUCTRL + LDA ppu_mask + STA PPUMASK + + LDA PPUSTATUS ; VDP interruption clear. + STA vdp_status + + ; Read controllers + LDA #$01 + STA CONT1 + STA cont_bits + LSR A + STA CONT1 + +.15: LDA CONT1 +; LSR A + AND #3 ; So it works with Famicom + CMP #1 + ROL cont_bits + BCC .15 + + JSR convert_joystick + STA joy1_data + STX key1_data + + LDA #$01 + STA CONT1 + STA cont_bits + LSR A + STA CONT1 + +.16: LDA CONT2 +; LSR A + AND #3 ; So it works with Famicom + CMP #1 + ROL cont_bits + BCC .16 + + JSR convert_joystick + STA joy2_data + STX key2_data + + if CVBASIC_MUSIC_PLAYER + LDA music_mode + BEQ .10 + JSR music_hardware +.10: + endif + INC frame + BNE .8 + INC frame+1 +.8: + INC lfsr ; Make LFSR more random + INC lfsr + INC lfsr + if CVBASIC_MUSIC_PLAYER + LDA ntsc + BEQ .12 + LDX music_tick + INX + CPX #6 + BNE .14 + LDX #0 +.14: STX music_tick + BEQ .9 +.12: + LDA music_mode + BEQ .9 + JSR music_generate +.9: + endif + ; This is like saving extra registers, because these + ; are used by the compiled code, and we don't want + ; any reentrancy. + LDA temp+0 + PHA + LDA temp+1 + PHA + LDA temp+2 + PHA + LDA temp+3 + PHA + LDA temp+4 + PHA + LDA temp+5 + PHA + LDA temp+6 + PHA + LDA temp+7 + PHA + + ;CVBASIC MARK DON'T CHANGE + PLA + STA temp+7 + PLA + STA temp+6 + PLA + STA temp+5 + PLA + STA temp+4 + PLA + STA temp+3 + PLA + STA temp+2 + PLA + STA temp+1 + PLA + STA temp+0 + + if CVBASIC_BANK_SWITCHING + PLA + ORA CHRRAM_BANK + STA BANKSEL + endif + PLA + TAY + PLA + TAX + PLA + RTI + +convert_joystick: + LDA #0 + LDX #15 + ROR cont_bits + BCC $+4 + ORA #2 + ROR cont_bits + BCC $+4 + ORA #8 + ROR cont_bits + BCC $+4 + ORA #4 + ROR cont_bits + BCC $+4 + ORA #1 + ROR cont_bits + BCC $+4 + LDX #11 + ROR cont_bits + BCC $+4 + LDX #10 + ROR cont_bits + BCC $+4 + ORA #$40 + ROR cont_bits + BCC $+4 + ORA #$80 + RTS + +wait: + LDA frame +.1: CMP frame + BEQ .1 + RTS + +print_string_cursor_constant: + PLA + STA temp + PLA + STA temp+1 + LDY #1 + LDA (temp),Y + STA cursor + INY + LDA (temp),Y + STA cursor+1 + INY + LDA (temp),Y + STA temp2 + TYA + CLC + ADC temp + STA temp + BCC $+4 + INC temp+1 + LDA temp2 + BNE print_string.2 + +print_string_cursor: + STA cursor + STY cursor+1 +print_string: + PLA + STA temp + PLA + STA temp+1 + LDY #1 + LDA (temp),Y + STA temp2 + INC temp + BNE $+4 + INC temp+1 +.2: CLC + ADC temp + TAY + LDA #0 + ADC temp+1 + PHA + TYA + PHA + INC temp + BNE $+4 + INC temp+1 + LDX ppu_pointer + LDA cursor + STA PPUBUF,X + LDA cursor+1 + AND #$07 + ORA #$20 + STA PPUBUF+1,X + LDA #0 + SEC + SBC temp2 + STA PPUBUF+2,X + LDA temp + SEC + SBC PPUBUF+2,X + STA PPUBUF+3,X + LDA temp+1 + SBC #0 + STA PPUBUF+4,X + TXA + CLC + ADC #5 + STA ppu_pointer + LDA temp2 + CLC + ADC cursor + STA cursor + BCC .1 + INC cursor+1 +.1: + CPX #PPUSIZE-5 + BCS .3 + RTS +.3: + JMP wait + +print_number: + LDX #0 + STX temp + SEI +print_number5: + LDX #10000 + STX temp2 + LDX #10000/256 + STX temp2+1 + JSR print_digit +print_number4: + LDX #1000 + STX temp2 + LDX #1000/256 + STX temp2+1 + JSR print_digit +print_number3: + LDX #100 + STX temp2 + LDX #0 + STX temp2+1 + JSR print_digit +print_number2: + LDX #10 + STX temp2 + LDX #0 + STX temp2+1 + JSR print_digit +print_number1: + LDX #1 + STX temp2 + STX temp + LDX #0 + STX temp2+1 + JSR print_digit + CLI + RTS + +print_digit: + LDX #$2F +.2: + INX + SEC + SBC temp2 + PHA + TYA + SBC temp2+1 + TAY + PLA + BCS .2 + CLC + ADC temp2 + PHA + TYA + ADC temp2+1 + TAY + PLA + CPX #$30 + BNE .3 + LDX temp + BNE .4 + RTS + +.4: DEX + BEQ .6 + LDX temp+1 + BNE print_char +.6: + LDX #$30 +.3: PHA + LDA #1 + STA temp + PLA + +print_char: + PHA + TYA + PHA + LDA cursor+1 + AND #$07 + ORA #$20 + TAY + LDA cursor + JSR WRTVRM + INC cursor + BNE .1 + INC cursor+1 +.1: + PLA + TAY + PLA + RTS + +mode_0: + JSR cls +clear_sprites: + LDA #$F0 + LDX #0 +.1: + STA $0200,X + INX + INX + INX + INX + BNE .1 + RTS + +music_init: + LDA #$40 + STA $4017 + LDA #$10 + STA $4000 ; Channel 1 silent + STA $4004 ; Channel 2 silent + STA $400C ; Channel 4 silent + LDA #$0b + STA $4015 ; Enable channel 1, 2 and 4. + LDA #$00 + STA $4010 + if CVBASIC_MUSIC_PLAYER + else + RTS + endif + + if CVBASIC_MUSIC_PLAYER + LDA #music_silence + LDY #music_silence>>8 + ; + ; Play music. + ; YA = Pointer to music. + ; +music_play: + SEI + STA music_pointer + STY music_pointer+1 + LDY #0 + STY music_note_counter + LDA (music_pointer),Y + STA music_timing + INY + STY music_playing + INC music_pointer + BNE $+4 + INC music_pointer+1 + LDA music_pointer + LDY music_pointer+1 + STA music_start + STY music_start+1 + if CVBASIC_BANK_SWITCHING + LDA $BFFF + STA music_bank + endif + CLI + RTS + + ; + ; Generates music + ; +music_generate: + LDA #$10 + STA audio_vol1 + STA audio_vol2 + STA audio_vol3 + STA audio_vol4hw + LDA music_note_counter + BEQ .1 + JMP .2 +.1: + if CVBASIC_BANK_SWITCHING + LDA music_bank + ORA CHRRAM_BANK + STA BANKSEL + endif + LDY #0 + LDA (music_pointer),Y + CMP #$fe ; End of music? + BNE .3 ; No, jump. + LDA #0 ; Keep at same place. + STA music_playing + RTS + +.3: CMP #$fd ; Repeat music? + BNE .4 + LDA music_start + LDY music_start+1 + STA music_pointer + STY music_pointer+1 + JMP .1 + +.4: LDA music_timing + AND #$3f ; Restart note time. + STA music_note_counter + + LDA (music_pointer),Y + CMP #$3F ; Sustain? + BEQ .5 + AND #$C0 + STA music_instrument_1 + LDA (music_pointer),Y + AND #$3F + ASL A + STA music_note_1 + LDA #0 + STA music_counter_1 +.5: + INY + LDA (music_pointer),Y + CMP #$3F ; Sustain? + BEQ .6 + AND #$C0 + STA music_instrument_2 + LDA (music_pointer),Y + AND #$3F + ASL A + STA music_note_2 + LDA #0 + STA music_counter_2 +.6: + INY + LDA (music_pointer),Y + CMP #$3F ; Sustain? + BEQ .7 + AND #$C0 + STA music_instrument_3 + LDA (music_pointer),Y + AND #$3F + ASL A + STA music_note_3 + LDA #0 + STA music_counter_3 +.7: + INY + LDA (music_pointer),Y + STA music_drum + LDA #0 + STA music_counter_4 + LDA music_pointer + CLC + ADC #4 + STA music_pointer + LDA music_pointer+1 + ADC #0 + STA music_pointer+1 +.2: + LDY music_note_1 + BEQ .8 + LDA music_instrument_1 + LDX music_counter_1 + JSR music_note2freq + STA audio_freq1 + STY audio_freq1+1 + STX audio_vol1 +.8: + LDY music_note_2 + BEQ .9 + LDA music_instrument_2 + LDX music_counter_2 + JSR music_note2freq + STA audio_freq2 + STY audio_freq2+1 + STX audio_vol2 +.9: + LDY music_note_3 + BEQ .10 + LDA music_instrument_3 + LDX music_counter_3 + JSR music_note2freq + STA audio_freq3 + STY audio_freq3+1 + STX audio_vol3 +.10: + LDA music_drum + BEQ .11 + CMP #1 ; 1 - Long drum. + BNE .12 + LDA music_counter_4 + CMP #3 + BCS .11 +.15: + LDA #$06 + STA audio_noise + LDA #$9c + STA audio_vol4hw + JMP .11 + +.12: CMP #2 ; 2 - Short drum. + BNE .14 + LDA music_counter_4 + CMP #0 + BNE .11 + LDA #$02 + STA audio_noise + LDA #$9c + STA audio_vol4hw + JMP .11 + +.14: ;CMP #3 ; 3 - Roll. + ;BNE + LDA music_counter_4 + CMP #2 + BCC .15 + ASL A + SEC + SBC music_timing + BCC .11 + CMP #4 + BCC .15 +.11: + LDX music_counter_1 + INX + CPX #$18 + BNE $+4 + LDX #$10 + STX music_counter_1 + + LDX music_counter_2 + INX + CPX #$18 + BNE $+4 + LDX #$10 + STX music_counter_2 + + LDX music_counter_3 + INX + CPX #$18 + BNE $+4 + LDX #$10 + STX music_counter_3 + + INC music_counter_4 + DEC music_note_counter + RTS + +music_flute: + LDA music_notes_table,Y + CLC + ADC .2,X + PHA + LDA music_notes_table+1,Y + ADC #0 + TAY + LDA .1,X + TAX + PLA + RTS + +.1: + db $9a,$9c,$9d,$9d,$9c,$9c,$9c,$9c + db $9b,$9b,$9b,$9b,$9a,$9a,$9a,$9a + db $9b,$9b,$9b,$9b,$9a,$9a,$9a,$9a + +.2: + db 0,0,0,0,0,1,1,1 + db 0,1,1,1,0,1,1,1 + db 0,1,1,1,0,1,1,1 + + ; + ; Converts note to frequency. + ; Input: + ; A = Instrument. + ; Y = Note (1-62) + ; X = Instrument counter. + ; Output: + ; YA = Frequency. + ; X = Volume. + ; +music_note2freq: + CMP #$40 + BCC music_piano + BEQ music_clarinet + CMP #$80 + BEQ music_flute + ; + ; Bass instrument + ; +music_bass: + LDA music_notes_table,Y + ASL A + PHA + LDA music_notes_table+1,Y + ROL A + TAY + LDA .1,X + TAX + PLA + RTS + +.1: + db $9d,$9d,$9c,$9c,$9b,$9b,$9a,$9a + db $99,$99,$98,$98,$97,$97,$96,$96 + db $95,$95,$94,$94,$93,$93,$92,$92 + +music_piano: + LDA music_notes_table,Y + PHA + LDA music_notes_table+1,Y + TAY + LDA .1,X + TAX + PLA + RTS + +.1: + db $dc,$db,$db,$da,$da,$d9,$d9,$d8 + db $d8,$d7,$d7,$d6,$d6,$d5,$d5,$d4 + db $d4,$d4,$d5,$d5,$d4,$d4,$d3,$d3 + +music_clarinet: + LDA music_notes_table,Y + CLC + ADC .2,X + PHA + LDA .2,X + BMI .3 + LDA #$00 + DB $2C +.3: LDA #$ff + ADC music_notes_table+1,Y + LSR A + TAY + LDA .1,X + TAX + PLA + ROR A + RTS + +.1: + db $1d,$1e,$1e,$1d,$1d,$1c,$1c,$1c + db $1b,$1b,$1b,$1b,$1c,$1c,$1c,$1c + db $1b,$1b,$1b,$1b,$1c,$1c,$1c,$1c + +.2: + db 0,0,0,0,-1,-1,-1,0 + db 1,1,1,0,-1,-1,-1,0 + db 1,1,1,0,-1,-1,-1,0 + + ; + ; Musical notes table. + ; +music_notes_table: + ; Silence - 0 + dw 0 + ; Values for 1.79 mhz. / 16, offset 0 + ; 2nd octave - Index 1 + dw 1710,1614,1524,1438,1357,1281,1209,1141,1077,1017,960,906 + ; 3rd octave - Index 13 + dw 855,807,762,719,679,641,605,571,539,508,480,453 + ; 4th octave - Index 25 + dw 428,404,381,360,339,320,302,285,269,254,240,226 + ; 5th octave - Index 37 + dw 214,202,190,180,170,160,151,143,135,127,120,113 + ; 6th octave - Index 49 + dw 107,101,95,90,85,80,76,71,67,64,60,57 + ; 7th octave - Index 61 + dw 53,50,48 + + ; + ; When the frequency upper byte is rewritten, the + ; output phase is reset, and it creates glitches. + ; So it doesn't rewrite frequency unless the note + ; changes. + ; +music_hardware: + LDA music_mode + CMP #4 ; PLAY SIMPLE? + BCC .9 ; Yes, jump. + LDA audio_vol2 + AND #$0F + BNE .9 + LDA audio_vol3 + AND #$0F + BEQ .9 + LDA audio_vol3 + STA audio_vol2 + LDA #$10 + STA audio_vol3 + LDA audio_freq3 + LDY audio_freq3+1 + STA audio_freq2 + STY audio_freq2+1 +.9: + LDA audio_freq1 + STA $4002 + LDA music_counter_1 + CMP #1 + BNE .3 + LDA audio_freq1+1 + ORA #$08 ; Keeps tone enabled + STA $4003 +.3: LDA audio_vol1 + STA $4000 + LDA #0 + STA $4001 + + LDA audio_freq2 + STA $4006 + LDA music_counter_2 + CMP #1 + BNE .4 + LDA audio_freq2+1 + ORA #$08 ; Keeps tone enabled + STA $4007 +.4: LDA audio_vol2 + STA $4004 + LDA #0 + STA $4005 + + LDA music_mode + CMP #4 ; PLAY SIMPLE? + BCC .6 ; Yes, jump. + + LSR audio_freq3+1 + ROR audio_freq3 + LDA audio_freq3 + STA $400A + LDA music_counter_3 + CMP #1 + BNE .5 + LDA audio_freq3+1 + ORA #$08 ; Keeps tone enabled + STA $400B +.5: + LDA #$20 + STA $4008 + LDA audio_vol3 + AND #$0F + BNE .1 + LDA #$0B + JMP .2 + +.1: LDA #$0F +.2: STA $4015 + +.6: LDA music_mode + LSR A ; NO DRUMS? + BCC .8 + LDA music_counter_4 + CMP #1 + BNE .7 + LDA audio_noise + STA $400E + ORA #$08 ; Keeps tone enabled + STA $400F +.7: LDA audio_vol4hw + STA $400C +.8: + RTS + +music_silence: + db 8 + db 0,0,0,0 + db -2 + endif + + + IF CVBASIC_BANK_SWITCHING +copy_chrram: + LDA #$00 + STA PPUADDR + STA PPUADDR + STA temp + STX temp+1 + LDX #32 +.1: + JSR copy_page + DEX + BNE .1 + RTS + +copy_page: + LDY #0 +.1: + LDA (temp),Y + STA PPUDATA + INY + BNE .1 + INC temp+1 + RTS + ENDIF + +START: + SEI + CLD + + BIT PPUSTATUS + + LDA #$40 + STA $4017 + LDA #$10 + STA $4000 ; Channel 1 silent + STA $4004 ; Channel 2 silent + STA $400C ; Channel 4 silent + LDA #$0b + STA $4015 ; Enable channel 1, 2 and 4. + LDA #$00 + STA $4010 + + LDA #0 + STA PPUCTRL + STA PPUMASK + + TAX +.1: STA $00,X + STA $0100,X + STA $0300,X + STA $0400,X + STA $0500,X + STA $0600,X + STA $0700,X + INX + BNE .1 + + LDX #STACK + TXS + + JSR clear_sprites + + ; + ; The NES starts with the PPU registers write-protected. + ; Around 29000 cycles must happen before these can be written. + ; + + ; lidnariq code for detecting NTSC/PAL/Dendy system + ; From: https://forums.nesdev.org/viewtopic.php?p=163258#p163258 + LDX #0 + LDY #0 + BIT PPUSTATUS + BPL $-3 +.5: + INX + BNE .6 + INY +.6: + ; About 27384 cycles passed at this time. + BIT PPUSTATUS + BPL .5 + LDA #0 + STA PPUCTRL + STA PPUMASK + + TYA + CMP #16 + BCC .7 + LSR A +.7: CLC + ADC #$F7 + CMP #3 + BCC .8 + LDA #3 ; Bad + ; 0=NTSC, 1=Pal, 2=Dendy, 3=Bad +.8: + CMP #0 ; Pass NTSC unchanged + BEQ .9 + LDA #1 ; All other PAL +.9: EOR #1 + STA ntsc + ; About 57165 cycles passed at this time. + + IF CVBASIC_BANK_SWITCHING + ; Copy CHRROM to CHRRAM + LDA #$00+CVBASIC_BANK_ROM_SIZE/16-3 + STA BANKSEL + LDX #$80 + JSR copy_chrram + + LDA #$20+CVBASIC_BANK_ROM_SIZE/16-3 + STA BANKSEL + LDX #$A0 + JSR copy_chrram + + LDA #$40+CVBASIC_BANK_ROM_SIZE/16-2 + STA BANKSEL + LDX #$80 + JSR copy_chrram + + LDA #$60+CVBASIC_BANK_ROM_SIZE/16-2 + STA BANKSEL + LDX #$A0 + JSR copy_chrram + + LDA #$00 ; BANK SELECT 1 + STA BANKSEL + ENDIF + + ; Clear 2K of pattern memory + LDA #$20 + STA PPUADDR + LDA #$00 + STA PPUADDR + LDX #$78 + LDA #$20 +.2: + STA PPUDATA + STA PPUDATA + STA PPUDATA + STA PPUDATA + STA PPUDATA + STA PPUDATA + STA PPUDATA + STA PPUDATA + DEX + BNE .2 + + LDX #$40 + LDA #$00 +.3: STA PPUDATA + DEX + BNE .3 + + ; Setup base palette + LDA #$3F + STA PPUADDR + LDA #$00 + STA PPUADDR + LDX #8 +.4: + LDA #$0F ; Black + STA PPUDATA + LDA #$35 ; Red + STA PPUDATA + LDA #$3A ; Green + STA PPUDATA + LDA #$30 ; White + STA PPUDATA + DEX + BNE .4 + + BIT PPUSTATUS + BPL $-3 + LDA #$1e ; Color normal, Sprites visible, Background visible, No clipping, Color. + STA PPUMASK + STA ppu_mask + LDA #$A8 ; Enable NMI, 8x16 sprites, BG=$0000, SPR=$1000, NAME=$2000 + STA ppu_ctrl + STA PPUCTRL + + JSR music_init + + JSR mode_0 + + LDA #$00 + STA joy1_data + STA joy2_data + LDA #$0F + STA key1_data + STA key2_data + diff --git a/cvbasic_prologue.asm b/cvbasic_prologue.asm index f515608..fd4f859 100644 --- a/cvbasic_prologue.asm +++ b/cvbasic_prologue.asm @@ -1,5 +1,5 @@ ; - ; CVBasic prologue (BASIC compiler for Colecovision) + ; CVBasic prologue (BASIC compiler for Colecovision and other consoles) ; ; by Oscar Toledo G. ; https://nanochess.org/ @@ -39,13 +39,45 @@ ; Revision date: Oct/15/2024. Added LDIRMV. Solved bug where asterisk and number ; keys values were inverted. ; Revision date: Nov/12/2024. Saves the VDP status. + ; Revision date: Feb/05/2026. Added support for spinners and roller controller + ; (Colecovision). + ; Revision date: Feb/09/2026. Added support for palette in MSX2. + ; Revision date: Feb/10/2026. Added support for sprites in MSX2 and 64K VRAM + ; access. + ; Revision date: Feb/26/2026. Faster CPYBLK, LDIRVM3, and FILVRM. + ; Revision date: Mar/09/2026. Corrections on joystick read for MSX. + ; Revision date: May/30/2026. Modified CLS and DEFINE SPRITE for MSX2 + ; extended video modes. + ; Revision date: Jun/14/2026. Corrected bug in SMS button handling for + ; second controller. Alternate frame wait + ; routine when Colecovision used with + ; spinner. ; + ; + ; Resources: + ; * Z80 instructions timing: https://map.grauw.nl/resources/z80instr.php + ; * Use the Timing Z80+M1 for MSX and Colecovision (both insert one + ; wait state in each M1 cycle) + ; + ; Technical requirements: + ; * Minimum of 28 cycles between VDP writes for 3.58 mhz. systems. + ; * Minimum of 31 cycles between VDP writes for 4.00 mhz. systems. + ; * For SMS minimum of 28 cycles between VDP writes (3.58 mhz) + ; * For MSX2 (V9938) minimum of 23 cycles between VDP writes (3.58 mhz) + ; + ; Dependances: + ; * LDIRVM, assumed to keep DE unchanged (target VRAM address) + ; * For speed. Required by CPYBLK and LDIRVM3. + ; + + if COLECO+SG1000+SMS JOYSEL: equ $c0 KEYSEL: equ $80 -JOY1: equ $fc-$20*SG1000 -JOY2: equ $ff-$22*SG1000 +JOY1: equ $fc-$20*(SG1000+SMS) +JOY2: equ $ff-$22*(SG1000+SMS) + endif if COLECO org $8000 @@ -73,18 +105,58 @@ JOY2: equ $ff-$22*SG1000 dw 0 dw START + if COLECO_SPINNER + jp wait + else jp 0 ; rst $08 + endif jp 0 ; rst $10 jp 0 ; rst $18 jp 0 ; rst $20 jp 0 ; rst $28 jp 0 ; rst $30 + if COLECO_SPINNER + jp spinner_handler + else jp 0 ; rst $38 - + endif jp nmi_handler + + if COLECO_SPINNER +spinner_handler: + PUSH AF + PUSH HL + LD A,$FF + LD (spinner_data),A + OUT (JOYSEL),A + PUSH AF + POP AF + LD HL,spinner_data+1 + IN A,(JOY1) + BIT 4,A + JR NZ,.1 + BIT 5,A + JR NZ,$+4 + INC (HL) + DB $3E + DEC (HL) +.1: INC HL + IN A,(JOY2) + BIT 4,A + JR NZ,.2 + BIT 5,A + JR NZ,$+4 + INC (HL) + DB $3E + DEC (HL) +.2: POP HL + POP AF + EI + RETI + endif endif endif - if SG1000+SVI + if SG1000+SMS+SVI org $0000 di ld sp,STACK @@ -236,6 +308,7 @@ RDPSG: endif if PV2000 + ; The Casio PV2000 has the VDP ports mapped into main memory (MREQ) WRTVDP: ld a,b ld (VDP+1),a @@ -281,12 +354,14 @@ FILVRM: call SETWRT pop af dec bc ; T-states (normal / M1) + inc c + inc b .1: ld (VDP),a ; 13 14 - dec bc ; 6 7 - bit 7,b ; 8 10 - jp z,.1 ; 10 11 + dec c ; 4 5 + jr nz,.1 ; 12 13 ; -- -- - ; 37 42 + ; 29 32 + djnz .1 ret LDIRMV: @@ -305,46 +380,90 @@ LDIRMV: ret LDIRVM: - EX DE,HL - CALL SETWRT - EX DE,HL - DEC BC - INC C - LD A,B - LD B,C + EX DE,HL + CALL SETWRT + EX DE,HL + DEC BC + INC C + LD A,B + LD B,C INC A - LD C,A + LD C,A .1: LD A,(HL) ; 7 8 LD (VDP),A ; 13 14 INC HL ; 6 7 - DJNZ .1 ; 13 14 - DEC C ; 4 5 - JP NZ,.1 ; 10 11 + DJNZ .1 ; 13 14 + DEC C ; 4 5 + JP NZ,.1 ; 10 11 RET else + ; Normal platforms with VDP connected via ports (IORQ) WRTVDP: + if COLECO_SPINNER + di + endif ld a,b out (VDP+1),a ld a,c or $80 out (VDP+1),a + if COLECO_SPINNER + ei + endif ret SETWRT: + if COLECO_SPINNER + di + endif + if MSX&2 + ld a,h + rlca + rlca + and 3 + out (VDP+1),a + ld a,$8e + out (VDP+1),a + ld a,l + out (VDP+1),a + ld a,h + and $3f + or $40 + out (VDP+1),a + else ld a,l out (VDP+1),a ld a,h or $40 out (VDP+1),a + endif + if COLECO_SPINNER + ei + endif ret SETRD: + if COLECO_SPINNER + di + endif + if MSX&2 + ld a,h + rlca + rlca + and 3 + out (VDP+1),a + ld a,$8e + out (VDP+1),a + endif ld a,l out (VDP+1),a ld a,h and $3f out (VDP+1),a + if COLECO_SPINNER + ei + endif ret WRTVRM: @@ -354,7 +473,7 @@ WRTVRM: out (VDP),a ret - if SG1000 + if SG1000+SMS db $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff ; Located at $0066 @@ -376,12 +495,23 @@ FILVRM: call SETWRT pop af dec bc ; T-states (normal / M1) + inc c + inc b + if MSX+COLECO +.1: out (VDP),a ; 12 + dec c ; 5 + jp nz,.1 ; 11 + ; -- + ; 28 + else .1: out (VDP),a ; 11 12 - dec bc ; 6 7 - bit 7,b ; 8 10 - jp z,.1 ; 10 11 + nop ; 4 5 + dec c ; 4 5 + jr nz,.1 ; 12 13 ; -- -- - ; 35 40 + ; 31 35 + endif + djnz .1 ret LDIRMV: @@ -392,7 +522,7 @@ LDIRMV: .1: in a,(VDP) ld (de),a - if SORD + if SORD+SMS nop endif if SG1000+MEMOTECH+EINSTEIN @@ -407,44 +537,65 @@ LDIRMV: ret LDIRVM: - ex de,hl - call SETWRT - ex de,hl - dec bc - inc c - ld a,b - ld b,c - inc a - ld c,VDP + ex de,hl + call SETWRT + ex de,hl + dec bc + inc c + ld a,b + ld b,c + inc a + ld c,VDP .1: - if SORD + if MSX&2 + otir ; 23 cycles per byte. + else + if SORD+SMS nop endif if SG1000+MEMOTECH+EINSTEIN nop ; SG1000 is 3.58 mhz, but SC3000 is 4 mhz. nop endif - outi - jp nz,.1 - dec a - jp nz,.1 + outi ; 18 + jp nz,.1 ; 11 + endif + dec a + jp nz,.1 ret endif + if SMS + else LDIRVM3: call .1 call .1 .1: push hl - push de push bc call LDIRVM pop bc - pop de ld a,d add a,8 ld d,a pop hl ret + endif + + ; Fill big buffers in 256-byte increments to avoid music pauses. +FILVRM2: + push af + push bc + push hl + call nmi_off + call FILVRM + call nmi_on + pop hl + pop bc + pop af + inc h + dec b + jp nz,FILVRM2 + ret DISSCR: call nmi_off @@ -470,11 +621,14 @@ CPYBLK: call nmi_off .1: push bc push hl - push de ld b,0 call LDIRVM - pop hl + ex de,hl + if SMS + ld bc,$0040 + else ld bc,$0020 + endif add hl,bc ex de,hl pop hl @@ -494,7 +648,7 @@ nmi_off: set 0,(hl) pop hl endif - if SG1000+MSX+SVI+SORD+MEMOTECH+NABU + if SG1000+SMS+MSX+SVI+SORD+MEMOTECH+NABU di endif ret @@ -511,7 +665,7 @@ nmi_on: pop hl pop af endif - if SG1000+MSX+SVI+SORD+MEMOTECH+NABU + if SG1000+SMS+MSX+SVI+SORD+MEMOTECH+NABU ei endif ret @@ -523,6 +677,60 @@ keypad_table: endif cls: + if SMS + ld hl,$3800 + ld (cursor),hl + di + call SETWRT +.1: ld a,$20 ; 7 + out (VDP),a ; 11 + inc hl ; 6 + inc hl ; 6 + ld a,$00 ; 7 + out (VDP),a ; 11 + ld a,h ; 4 + cp $3e ; 7 + jp nz,.1 ; 10 + ei + ret + else + if MSX&2 + ld a,(mode) + and $20 + jr z,.2 + ld hl,$0000 + ld (cursor),hl + ld bc,(cls_size) + ld a,(msx2_mode) + cp 2 + jp z,FILVRM2 + dec a + ld a,(msx2_bk_color) + jp z,.3 + ; Extend 4-bit color to 8-bit. + ld b,a + rlca + rlca + rlca + rlca + or b + jp FILVRM2 + ; Extend 2-bit color to 8-bit. +.3: + and $03 + ld b,a + rlca + rlca + or b + ld b,a + rlca + rlca + rlca + rlca + or b + ret +.2: + endif ld hl,$1800 ld (cursor),hl ld bc,$0300 @@ -530,23 +738,49 @@ cls: call nmi_off call FILVRM jp nmi_on + endif print_string: ld c,a ld b,0 ld de,(cursor) + if SMS + ld a,d + and $07 + or $38 + ld d,a + else ld a,d and $07 or $18 ld d,a + endif push de push bc call nmi_off + if SMS + ex de,hl +.1: ld a,(de) + call WRTVRM + inc de + inc hl + xor a + call WRTVRM + inc hl + dec bc + ld a,b + or c + jp nz,.1 + else call LDIRVM + endif call nmi_on pop bc pop hl add hl,bc + if SMS + add hl,bc + endif ld (cursor),hl ret @@ -586,33 +820,47 @@ print_digit: dec a jr z,.4 ld a,c - jr .5 + jr print_char .4: ld a,$30 .3: ld b,1 -.5: push hl + +print_char: + push hl ld hl,(cursor) ex af,af' ld a,h and $07 + if SMS + or $38 + else or $18 + endif ld h,a ex af,af' call WRTVRM inc hl + if SMS + xor a + call WRTVRM + inc hl + endif ld (cursor),hl pop hl ret -define_sprite: + ; Support routine for DEFINE CHAR. +define_char: ex de,hl ld l,a ld h,0 add hl,hl ; x2 add hl,hl ; x4 add hl,hl ; x8 + if SMS add hl,hl ; x16 add hl,hl ; x32 + endif ld c,l ld b,h pop af @@ -620,16 +868,34 @@ define_sprite: push af add hl,hl ; x2 add hl,hl ; x4 - ld h,$07 add hl,hl ; x8 + if SMS add hl,hl ; x16 add hl,hl ; x32 + endif ex de,hl - call nmi_off + if SMS + di call LDIRVM + ei + ret + else + call nmi_off + ld a,(mode) + and 8 + jr nz,.1 + call LDIRVM3 jp nmi_on -define_char: +.1: call LDIRVM + jp nmi_on + endif + + ; Support routine for DEFINE COLOR. +define_color: + if SMS + ret + else ex de,hl ld l,a ld h,0 @@ -645,23 +911,25 @@ define_char: add hl,hl ; x4 add hl,hl ; x8 ex de,hl + set 5,d call nmi_off - ld a,(mode) - and 8 - jr nz,.1 call LDIRVM3 jp nmi_on - -.1: call LDIRVM - jp nmi_on + endif -define_color: + ; Support routine for DEFINE SPRITE. +define_sprite: ex de,hl ld l,a ld h,0 add hl,hl ; x2 add hl,hl ; x4 add hl,hl ; x8 + add hl,hl ; x16 + add hl,hl ; x32 + if SMS + add hl,hl ; x64 + endif ld c,l ld b,h pop af @@ -669,14 +937,50 @@ define_color: push af add hl,hl ; x2 add hl,hl ; x4 + if SMS + set 1,h + else + if MSX&2 + ld a,(mode) + and $20 ; Extended video modes? + ld h,$07 ; $3800 for MODE 0-4. + jr z,$+4 ; No, jump. + ld h,$1c ; $f000 for MODE 5-8. + else + ld h,$07 + endif + endif add hl,hl ; x8 + add hl,hl ; x16 + add hl,hl ; x32 + if SMS + add hl,hl ; x64 + endif ex de,hl - set 5,d call nmi_off - call LDIRVM3 + call LDIRVM jp nmi_on + endif update_sprite: + if SMS + pop bc ; Pop return address. + pop de ; 3th. argument in D (Y-coordinate) + ld e,a ; 4th. argument in E (frame) + pop af ; 2nd. argument in A (X-coordinate) + pop hl ; 1st. argument in H (sprite number) + push bc ; Push return address. + ld l,h + res 7,l + res 6,l + ld h,sprites>>8 + ld (hl),a + sla l + set 7,l + ld (hl),d + inc l + ld (hl),e + else pop bc ld (sprite_data+3),a pop af @@ -686,8 +990,9 @@ update_sprite: pop af ld (sprite_data),a pop af + ; A = Sprite number push bc - ld de,sprites + ld d,sprites>>8 add a,a add a,a if SORD @@ -695,26 +1000,89 @@ update_sprite: endif ld e,a ld hl,sprite_data - ld bc,4 - ldir + ldi + ldi + ldi + ldi + endif ret - ; Fast 16-bit multiplication. + ; + ; Fast 16-bit multiplication by Oscar Toledo G. + ; Input: + ; HL = Operand 1. + ; DE = Operand 2. + ; Output: + ; HL = HL x DE. + ; + ; This is a trick I've discovered. + ; 1. The smallest operand goes into DE. + ; The biggest operand goes into BC. + ; HL becomes zero. + ; 2. If D is non-zero, calculate H = D x C with 8-bit arithmetic. + ; B can be ignored as this routine doesn't generate a 24-bit result. + ; 3. Calculate HL = HL + E x BC with early exit as it shifts E to the right. + ; _mul16: - ld b,h - ld c,l - ld a,16 - ld hl,0 -.1: - srl d - rr e - jr nc,.2 - add hl,bc -.2: sla c - rl b - dec a - jp nz,.1 - ret + or a ; 5 + sbc hl,de ; 17 + add hl,de ; 12 + jr nc,$+3 ; 13/8 + ex de,hl ; 5 Smallest operand in DE. + + ld b,h ; 5 + ld c,l ; 5 + ld hl,0 ; 11 + ld a,d + or a ; High-byte is zero? + jp z,.2 ; Yes, jump. + xor a ; 5 + sla d ; 10 + jp nc,$+4 ; 11 + add a,c ; 5 + add a,a ; 5 + sla d ; 10 + jp nc,$+4 ; 11 + add a,c ; 5 + add a,a ; 5 + sla d ; 10 + jp nc,$+4 ; 11 + add a,c ; 5 + add a,a ; 5 + sla d ; 10 + jp nc,$+4 ; 11 + add a,c ; 5 + add a,a ; 5 + sla d ; 10 + jp nc,$+4 ; 11 + add a,c ; 5 + add a,a ; 5 + sla d ; 10 + jp nc,$+4 ; 11 + add a,c ; 5 + add a,a ; 5 + sla d ; 10 + jp nc,$+4 ; 11 + add a,c ; 5 + add a,a ; 5 + sla d ; 10 + jp nc,$+4 ; 11 + add a,c ; 5 + ld h,a ; 5 +.2: ; +.3: srl e ; 10 + jp nc,$+4 ; 11 + add hl,bc ; 12 + ret z ; 6/12 + sla c ; 10 + rl b ; 10 + srl e ; 10 + jp nc,$+4 ; 11 + add hl,bc ; 12 + ret z ; 6/12 + sla c ; 10 + rl b ; 10 + jp .3 ; 11 ; 16-bit signed modulo. ; hl = hl % de @@ -766,21 +1134,7 @@ _neg16: ; Fast 16-bit division. ; hl = hl / de _div16: - ld b,h - ld c,l - ld hl,0 - ld a,16 -.1: - rl c - rl b - adc hl,hl - sbc hl,de - jp nc,.2 - add hl,de -.2: - ccf - dec a - jp nz,.1 + call _mod16 rl c rl b ld h,b @@ -792,16 +1146,32 @@ _mod16: ld b,h ld c,l ld hl,0 - ld a,16 + + ; Save cycles if top byte is zero. + ld a,b + or a + ld a,8 + jr nz,.1 + rrca + ld b,c + ld c,0 .1: rl c rl b adc hl,hl sbc hl,de - jp nc,.2 + jp nc,$+4 + add hl,de + ccf + + rl c + rl b + adc hl,hl + sbc hl,de + jp nc,$+4 add hl,de -.2: ccf + dec a jp nz,.1 ret @@ -851,7 +1221,7 @@ random: ret sn76489_freq: - if COLECO+SG1000+SORD+MEMOTECH+PV2000 + if COLECO+SG1000+SMS+SORD+MEMOTECH+PV2000 ld b,a ld a,l and $0f @@ -874,7 +1244,7 @@ sn76489_freq: ret sn76489_vol: - if COLECO+SG1000+SORD+MEMOTECH+PV2000 + if COLECO+SG1000+SMS+SORD+MEMOTECH+PV2000 cpl and $0f or b @@ -886,7 +1256,7 @@ sn76489_vol: ret sn76489_control: - if COLECO+SG1000+SORD+MEMOTECH+PV2000 + if COLECO+SG1000+SMS+SORD+MEMOTECH+PV2000 and $0f or $e0 out (PSG),a @@ -905,7 +1275,7 @@ ay3_reg: out ($51),a ret endif - if SG1000+SORD+MEMOTECH+PV2000 + if SG1000+SORD+SMS+MEMOTECH+PV2000 ret endif if MSX+SVI+EINSTEIN+NABU @@ -930,7 +1300,7 @@ ay3_freq: pop af ret endif - if SG1000+SORD+MEMOTECH+PV2000 + if SG1000+SMS+SORD+MEMOTECH+PV2000 ret endif if MSX+SVI+EINSTEIN+NABU @@ -941,8 +1311,8 @@ ay3_freq: jp WRTPSG endif - if SG1000+SVI+SORD+MEMOTECH+EINSTEIN+PV2000+NABU - ; Required for SG1000 as it doesn't have a BIOS + if SG1000+SMS+SVI+SORD+MEMOTECH+EINSTEIN+PV2000+NABU + ; Required for SG1000 and Sega Master System as both don't have a BIOS ; Required for SVI because we don't have access to BIOS in cartridge. ; Required for Sord M5 because it doesn't provide an ASCII charset. ; Required for Memotech/Einstein because CP/M uses the memory. @@ -1051,6 +1421,206 @@ font_bitmaps: db $70,$70,$20,$f8,$20,$70,$50,$00 ; $7f endif + if MSX+SMS + if FM_SUPPORT + ; + ; Program FM instrument + ; +program_fm: + ld de,fm_data + ld bc,8 + ldir + ld a,1 + ld (fm_new),a + ; Cannot load instrument here, as it could imply bank switching. + ret + endif + endif + + if MSX +palette_default: + ld hl,msx2_default_palette +palette_load: + ld a,($002d) + or a + ret z + ld bc,$0010 + di + call WRTVDP + ld bc,32*256+VDP+2 + otir + ei + ret + +msx2_default_palette: + dw $000,$000,$611,$733,$117,$327,$151,$627 + dw $171,$373,$661,$664,$411,$265,$555,$777 + endif + + if MSX&2 +define_sprite_color: + ex de,hl + add a,a ; x2 + add a,a ; x4 + add a,a ; x8 + add a,a ; x16 + ld c,a + ld b,0 + rl b + pop af + pop hl + push af + add hl,hl ; x2 + add hl,hl ; x4 + add hl,hl ; x8 + ld h,$f8/2 + add hl,hl ; x16 + ex de,hl + di + call LDIRVM + ei + ret + +update_sprite2: + pop bc + ld (sprite_data+2),a + pop af + ld (sprite_data+1),a + pop af + ld (sprite_data),a + pop af + ; A = Sprite number + push bc + ld d,sprites>>8 + add a,a + add a,a + ld e,a + ld hl,sprite_data + ldi + ldi + ldi + ret + + ; + ; Register that must be set for V9938 (MSX2) because larger VRAM: + ; Reg. 2 = 0 A16 A15 A14 A13 A12 A11 A10 Pattern name table. + ; Reg. 3 = A13 1 1 1 1 1 1 1 Color table (reg. D) + ; Reg. 10= 0 0 0 0 0 A16 A15 A14 Color table high bits. + ; Reg. 4 = 0 0 A16 A15 A14 A13 1 1 Bitmap table (reg. E) + ; Reg. 5 = A14 A13 A12 A11 A10 A9 1 1 Sprite attribute table... + ; Reg. 11= 0 0 0 0 0 0 A16 A15 ...and high bits. + ; Reg. 6 = 0 0 A16 A15 A14 A13 A12 A11 Sprite pattern generator + ; +mode_4: + ld hl,mode + res 3,(hl) + set 4,(hl) + if MSX&2 + res 5,(hl) + endif + ld bc,$0400 ; Normal mode but with enhanced sprites enabled. + ld de,$ff03 ; $2000 for color table, $0000 for bitmaps. + call vdp_generic_mode + ld bc,$000a ; Reg. 10 (high bits of color table) + call WRTVDP + ; The value $f4 seems logical but it makes colors + ; for sprites 0-7 replicate in sprites 8-15, + ; 16-23, and 24-31. Not described in Yamaha docs, + ; but emulated by OpenMSX and BlueMSX. + ld bc,$f705 ; $fa00 for sprite attribute table... + call WRTVDP ; ...so sprite colors appear at $f800-$f9ff. + ld bc,$010b ; Reg. 11 (high bits of sprite attribute table) + call WRTVDP + ld hl,($0004) + inc h + ld de,$0100 + ld bc,$0300 + call LDIRVM3 + call nmi_on + ld hl,$2000 + ld bc,$1800 + ld a,$f0 + call FILVRM2 + call cls + jp vdp_generic_sprites + endif + + if SMS +palette_default: + ld hl,sms_default_palette +palette_load: + push hl + di + ld hl,$c000 + call SETWRT + pop hl + ld bc,32*256+VDP + outi + jp nz,$-2 + ei + ret + + ; TMS9118-alike palette +sms_default_palette: + db $00,$00,$0c,$2e,$20,$30,$02,$3c,$17,$2B,$0f,$2f,$08,$33,$2a,$3f + db $00,$00,$0c,$2e,$20,$30,$02,$3c,$17,$2B,$0f,$2f,$08,$33,$2a,$3f + +mode_4: + di + ld bc,$0400 + call WRTVDP + ld bc,$a201 + call WRTVDP + ld bc,$0f02 ; $3800 for pattern table (required bit 0 set to 1 for SMS1) + call WRTVDP + ld bc,$ff03 ; Not used (required value for SMS1) + call WRTVDP + ld bc,$0704 ; Not used (required value for SMS1) + call WRTVDP + ld bc,$7f05 ; $3f00 for sprite attribute table (required bit 0 set to 1) + call WRTVDP + ld bc,$0706 ; $2000 for sprites bitmaps (or $03 for $0000) + call WRTVDP + ld bc,$0007 + call WRTVDP + ld bc,$0008 ; Background X scroll + call WRTVDP + ld bc,$0009 ; Background Y scroll + call WRTVDP + ld hl,$20*32 ; Point to space character bitmap + call SETWRT + ld hl,font_bitmaps + ld c,96 +.1: ld b,8 +.2: ld a,(hl) + out (VDP),a + ld a,(hl) + out (VDP),a + ld a,(hl) + out (VDP),a + ld a,(hl) + out (VDP),a + inc hl + djnz .2 + dec c + jp nz,.1 + ei + call cls + di + ld hl,$3f00 + ld bc,$0040 + ld a,$e0 + call FILVRM + ld hl,sprites + ld de,sprites+1 + ld bc,63 + ld (hl),$e0 + ldir + ei + call palette_default + jp ENASCR + + else vdp_generic_mode: call nmi_off call WRTVDP @@ -1062,7 +1632,7 @@ vdp_generic_mode: ld c,$03 ; for color table. call WRTVDP ld b,e - ld c,$04 ; for bitmap table. + ld c,$04 ; for bitmap table call WRTVDP ld bc,$3605 ; $1b00 for sprite attribute table. call WRTVDP @@ -1074,6 +1644,10 @@ vdp_generic_mode: mode_0: ld hl,mode res 3,(hl) + res 4,(hl) + if MSX&2 + res 5,(hl) + endif ld bc,$0200 ld de,$ff03 ; $2000 for color table, $0000 for bitmaps. call vdp_generic_mode @@ -1097,47 +1671,49 @@ mode_0: ld bc,$0300 call LDIRVM3 call nmi_on - call nmi_off ld hl,$2000 ld bc,$1800 ld a,$f0 - call FILVRM - call nmi_on + call FILVRM2 call cls vdp_generic_sprites: call nmi_off + ld a,(mode) + bit 4,a ld hl,$1b00 - ld bc,$0080 ld a,$d1 + jr z,$+6 + ld h,$fa + ld a,$e0 + ld bc,$0080 call FILVRM ld hl,sprites ld de,sprites+1 ld bc,127 - ld (hl),$d1 + ld (hl),a ldir call nmi_on - call nmi_off - ld bc,$e201 ; Enable screen and interrupts. - call WRTVDP - jp nmi_on + jp ENASCR mode_1: ld hl,mode res 3,(hl) + res 4,(hl) + if MSX&2 + res 5,(hl) + endif ld bc,$0200 ld de,$ff03 ; $2000 for color table, $0000 for bitmaps. call vdp_generic_mode + call nmi_on ld hl,$0000 ld bc,$1800 xor a - call FILVRM - call nmi_on - call nmi_off + call FILVRM2 ld hl,$2000 ld bc,$1800 ld a,$f0 - call FILVRM - call nmi_on + call FILVRM2 ld hl,$1800 .1: call nmi_off ld b,32 @@ -1154,6 +1730,10 @@ mode_1: mode_2: ld hl,mode set 3,(hl) + res 4,(hl) + if MSX&2 + res 5,(hl) + endif ld bc,$0000 ld de,$8000 ; $2000 for color table, $0000 for bitmaps. call vdp_generic_mode @@ -1166,7 +1746,7 @@ mode_2: ld de,-128 add hl,de endif - if SG1000+SVI+SORD+MEMOTECH+EINSTEIN+PV2000+NABU + if SG1000+SMS+SVI+SORD+MEMOTECH+EINSTEIN+PV2000+NABU ld hl,font_bitmaps endif if MSX @@ -1177,14 +1757,13 @@ mode_2: ld bc,$0300 call LDIRVM call nmi_on - call nmi_off ld hl,$2000 ld bc,$0020 ld a,$f0 call FILVRM - call nmi_on call cls jp vdp_generic_sprites + endif if MSX ENASLT: EQU $0024 ; Select slot (H=Addr, A=Slot) @@ -1252,8 +1831,8 @@ nmi_handler: if COLECO ld a,($ffbf) endif - if SG1000 - ld a,($7fff) + if SG1000+SMS + ld a,($7fbf) endif if MSX ld a,($bfff) @@ -1263,18 +1842,82 @@ nmi_handler: if SORD+MEMOTECH call ctc_reti endif - if SG1000+MSX+SVI+SORD+MEMOTECH+NABU + if SG1000+SMS+MSX+SVI+SORD+MEMOTECH+NABU in a,(VDPR+1) ld (vdp_status),a endif - ld bc,$8000+VDP + + ; + ; Update of sprite attribute table + ; + if SMS bit 2,(hl) jr z,.4 + ld hl,$3f00 + call SETWRT + ld hl,sprites + ld bc,$4000+VDP + outi + jp nz,$-2 + ld hl,$3f80 + call SETWRT + ld hl,sprites+128 + ld bc,$8000+VDP + outi + jp nz,$-2 + jr .5 +.4: ld hl,$3f00 + call SETWRT + ld a,(flicker) + inc a + and $3f + ld (flicker),a + ld l,a + ld h,sprites>>8 + ld bc,$4000+VDP + ld de,6 + outi + add hl,de + res 6,l + jp nz,$-5 + ld hl,$3f80 + call SETWRT + ld a,(flicker) + add a,a + or $80 + ld l,a + ld bc,$8000+VDP + ld de,12 + ld h,sprites>>8 + outi + nop + nop + nop + outi + add hl,de + set 7,l + jp nz,$-12 +.5: + else + ld bc,$8000+VDP + bit 2,(hl) + jr z,.4 + bit 4,(hl) ld hl,$1b00 + jr z,$+4 +.10: + ld h,$fa call SETWRT ld hl,sprites .7: + if MSX&2 + outi + outi + outi + outi + jp nz,.7 + else if PV2000 ld a,(hl) ld (VDP),a @@ -1288,10 +1931,13 @@ nmi_handler: outi jp nz,.7 endif + endif jr .5 .4: + bit 4,(hl) ld hl,$1b00 + jr nz,.10 ; No flicker sprites in MSX2 (MODE 4) call SETWRT ld a,(flicker) add a,$04 @@ -1356,8 +2002,12 @@ nmi_handler: add hl,de jp nz,.6 .5: + endif if COLECO + if COLECO_SPINNER + DI + endif out (JOYSEL),a ex (sp),hl ex (sp),hl @@ -1365,15 +2015,24 @@ nmi_handler: or $b0 ld b,a in a,(JOY2) + if COLECO_SPINNER + EI + endif or $b0 ld c,a + if COLECO_SPINNER + DI + endif out (KEYSEL),a ex (sp),hl ex (sp),hl in a,(JOY1) ld d,a in a,(JOY2) + if COLECO_SPINNER + EI + endif ld e,a ld a,d @@ -1407,19 +2066,19 @@ nmi_handler: ld a,(hl) ld (key2_data),a endif - if SG1000 + if SG1000+SMS ld a,$07 out ($de),a ld b,$ff - in a,(JOY1) + in a,(JOY1) ; Port $dc ld h,a - in a,(JOY2) + in a,(JOY2) ; Port $de ld l,a in a,($de) - cp 7 - jp nz,.sg1000 + cp 7 ; SC3000 keyboard? + jp nz,.sg1000 ; No, jump. - ld a,$00 + ld a,$00 ; Row 0 out ($de),a in a,($dc) rra @@ -1429,7 +2088,7 @@ nmi_handler: rra ld c,8 jr nc,.sg1 - ld a,$01 + ld a,$01 ; Row 1 out ($de),a in a,($dc) rra @@ -1439,7 +2098,7 @@ nmi_handler: rra ld c,9 jr nc,.sg1 - ld a,$02 + ld a,$02 ; Row 2 out ($de),a in a,($dc) rra @@ -1449,7 +2108,7 @@ nmi_handler: rra ld c,0 jr nc,.sg1 - ld a,$03 + ld a,$03 ; Row 3 out ($de),a in a,($dc) bit 4,a @@ -1458,13 +2117,13 @@ nmi_handler: rra ld c,4 jr nc,.sg1 - ld a,$04 + ld a,$04 ; Row 4 out ($de),a in a,($dc) rra ld c,5 jr nc,.sg1 - ld a,$05 + ld a,$05 ; Row 5 out ($de),a in a,($dc) bit 6,a @@ -1473,7 +2132,7 @@ nmi_handler: rra ld c,6 jr nc,.sg1 - ld a,$06 + ld a,$06 ; Row 6 out ($de),a in a,($dc) rra @@ -1517,22 +2176,22 @@ nmi_handler: jr nz,$+4 res 5,h .sg1000: - bit 0,h + bit 0,h ; Port A - Up jr nz,$+4 res 0,b - bit 1,h + bit 1,h ; Port A - Down jr nz,$+4 res 2,b - bit 2,h + bit 2,h ; Port A - Left jr nz,$+4 res 3,b - bit 3,h + bit 3,h ; Port A - Right jr nz,$+4 res 1,b - bit 4,h + bit 4,h ; Port A - TL jr nz,$+4 res 6,b - bit 5,h + bit 5,h ; Port A - TR jr nz,$+4 res 7,b ld a,b @@ -1540,25 +2199,25 @@ nmi_handler: ld (joy1_data),a ld a,$ff - bit 6,h + bit 6,h ; Port B - Up jr nz,$+4 res 0,a - bit 7,h + bit 7,h ; Port B - Down jr nz,$+4 res 2,a - bit 0,l + bit 0,l ; Port B - Left jr nz,$+4 res 3,a - bit 1,l + bit 1,l ; Port B - Right jr nz,$+4 res 1,a - bit 2,l + bit 2,l ; Port B - TL jr nz,$+4 - res 4,a - bit 3,l + res 6,a + bit 3,l ; Port B - TR jr nz,$+4 - res 5,a + res 7,a cpl ld (joy2_data),a @@ -1567,8 +2226,8 @@ nmi_handler: ; Keyboard matrix from https://map.grauw.nl/articles/keymatrix.php ld a,15 call RDPSG - and $b0 - or $4f + and $80 + or $5c ; Read joystick 2. ld e,a ld a,15 call WRTPSG @@ -1667,8 +2326,8 @@ nmi_handler: ld a,15 call RDPSG - and $b0 - or $0f + and $80 + or $23 ; Read joystick 1. ld e,a ld a,15 call WRTPSG @@ -2350,11 +3009,17 @@ nmi_handler: ld h,$ff ld a,(hl) endif - if SG1000 + if SG1000+SMS ld ($fffe),a endif if MSX + if KONAMI + ld ($8000),a + inc a + ld ($a000),a + else ld ($7000),a + endif endif endif pop de @@ -2366,7 +3031,7 @@ nmi_handler: pop af retn endif - if SG1000+SVI+NABU + if SG1000+SMS+SVI+NABU pop af ei reti @@ -2417,53 +3082,53 @@ keyboard_handler: .8: ld (nabu_data2),a jr .1 .7: - cp $a0 + cp $a0 ; Is it data for joystick? jr c,.2 cp $c0 - jr c,.joystick + jr c,.joystick ; Yes, jump. .2: ld hl,nabu_data0 cp $80 jr nz,.3 - res 4,(hl) + res 4,(hl) ; Now data for joystick 1. jr .1 .3: cp $81 jr nz,.4 - set 4,(hl) + set 4,(hl) ; Now data for joystick 2. jr .1 .4: - cp $e0 - jr c,.1 - and $0f + cp $e0 ; Keys with up/down event? + jr c,.1 ; No, jump. + and $0f ; Arrow right ld b,2 jr z,.5 - dec a + dec a ; Arrow left ld b,8 jr z,.5 - dec a + dec a ; Arrow up ld b,1 jr z,.5 - dec a + dec a ; Arrow down ld b,4 jr z,.5 - dec a + dec a ; |||> ld b,$80 jr z,.5 - dec a + dec a ; <||| ld b,$40 jr nz,.1 .5: bit 4,c - jr nz,.6 + jr nz,.6 ; Jump if depress event. ld a,(hl) - or b + or b ; Key pressed. ld (hl),a jr .1 .6: ld a,b cpl - and (hl) + and (hl) ; Key depressed. ld (hl),a jr .1 @@ -2496,13 +3161,13 @@ keyboard_handler: ei reti endif - if SORD + if SORD+(COLECO&COLECO_SPINNER) wait: - ld de,(frame) + ld a,(frame) + ld d,a .1: - ld hl,(frame) - or a - sbc hl,de + ld a,(frame) + cp d jr z,.1 ret endif @@ -2516,7 +3181,6 @@ wait: ld (vdp_status),a jp nmi_handler endif - ; ; The music player code comes from my ; game Princess Quest for Colecovision (2012) @@ -2526,7 +3190,7 @@ wait: ; Init music player. ; music_init: - if COLECO+SG1000+SORD+MEMOTECH+PV2000 + if COLECO+SG1000+SMS+SORD+MEMOTECH+PV2000 ld a,$9f out (PSG),a if MEMOTECH @@ -2552,8 +3216,15 @@ music_init: if MEMOTECH in a,($03) endif + if FM_SUPPORT + ld a,7 + ld ($fffe),a + ld a,(fm_slot) + inc a + call nz,init_fm + endif endif - if COLECO+SG1000+MSX+SVI+SORD+MEMOTECH+PV2000 + if COLECO+SG1000+SMS+MSX+SVI+SORD+MEMOTECH+PV2000 MIX_BASE: equ $b8 endif if EINSTEIN+NABU @@ -2572,6 +3243,20 @@ MIX_BASE: equ $78 ld a,$07 ld e,MIX_BASE call WRTPSG + if FM_SUPPORT + if KONAMI + ld a,14 + ld ($8000),a + inc a + ld ($a000),a + else + ld a,7 + ld ($7000),a + endif + ld a,(fm_slot) + inc a + call nz,init_fm + endif endif if SGM ld b,$08 @@ -2617,8 +3302,8 @@ music_play: if COLECO ld a,($ffbf) endif - if SG1000 - ld a,($7fff) + if SG1000+SMS + ld a,($7fbf) endif if MSX ld a,($bfff) @@ -2643,6 +3328,98 @@ music_generate: ld a,$ff ld (audio_vol4hw),a + if FM_SUPPORT + ld a,(fm_enabled) + or a + jp z,.fm1 + ld a,(fm_slot) + inc a + jp z,.fm1 + if MSX + if KONAMI + ld a,14 + ld ($8000),a + inc a + ld ($a000),a + else + ld a,7 + ld ($7000),a + endif + endif + if SMS + ld a,7 + ld ($fffe),a + endif + ld a,(music_note_counter) + or a + jp nz,music_generate_fm.6 + ld hl,(music_pointer) +.fm15: push hl + if CVBASIC_BANK_SWITCHING + ld a,(music_bank) + if SG1000+SMS + ld ($fffe),a + endif + if MSX + if KONAMI + ld ($8000),a + inc a + ld ($a000),a + else + ld ($7000),a + endif + endif + endif + ld b,(hl) + inc hl + ld c,(hl) + inc hl + ld d,(hl) + inc hl + ld e,(hl) + pop hl + ld a,(music_timing) + rlca + jr nc,.fm16 + ld e,d + ld d,0 + jr .fm17 + +.fm16: rlca + jr nc,.fm17 + ld e,0 +.fm17: ld a,b ; Read first byte. + cp -2 ; End of music? + jr nz,.fm19 ; No, jump. + xor a ; Keep at same place. + ld (music_playing),a + ret + +.fm19: cp -3 ; Repeat music? + push af + if MSX + if KONAMI + ld a,14 + ld ($8000),a + inc a + ld ($a000),a + else + ld a,7 + ld ($7000),a + endif + endif + if SMS + ld a,7 + ld ($fffe),a + endif + pop af + jp nz,music_generate_fm.0 + ld hl,(music_start) + jr .fm15 + +.fm1: + endif + ld a,(music_note_counter) or a jp nz,.6 @@ -2655,11 +3432,17 @@ music_generate: ld d,$ff ld a,(de) endif - if SG1000 + if SG1000+SMS ld ($fffe),a endif if MSX + if KONAMI + ld ($8000),a + inc a + ld ($a000),a + else ld ($7000),a + endif endif endif ld b,(hl) @@ -2965,7 +3748,7 @@ music_flute: ; Emit sound. ; music_hardware: - if COLECO+SG1000+SORD+MEMOTECH+PV2000 + if COLECO+SG1000+SMS+SORD+MEMOTECH+PV2000 ld a,(music_mode) cp 4 ; PLAY SIMPLE? jr c,.7 ; Yes, jump. @@ -3175,7 +3958,7 @@ music_hardware: ; Enable drum. ; enable_drum: - if COLECO+SG1000+SORD+MEMOTECH+PV2000 + if COLECO+SG1000+SMS+SORD+MEMOTECH+PV2000 ld a,$f5 ld (audio_vol4hw),a else @@ -3226,7 +4009,7 @@ music_notes_table: dw 53,50,48 endif - if COLECO+SG1000+SORD+MEMOTECH+PV2000 + if COLECO+SG1000+SMS+SORD+MEMOTECH+PV2000 ; ; Converts AY-3-8910 volume to SN76489 ; @@ -3249,12 +4032,21 @@ define_char_unpack: add hl,hl ; x2 add hl,hl ; x4 add hl,hl ; x8 + if SMS + add hl,hl ; x16 + add hl,hl ; x32 + endif ex de,hl + if SMS + else ld a,(mode) and 8 jp z,unpack3 + endif jp unpack + if SMS + else define_color_unpack: ex de,hl pop af @@ -3278,6 +4070,7 @@ unpack3: add a,8 ld d,a ret + endif ; ; Pletter-0.5c decompressor (XL2S Entertainment & Team Bomba) @@ -3425,9 +4218,18 @@ unpack: endif START: - if SVI+SG1000 + if SVI+SG1000+SMS im 1 endif + if SMS + if FM_SUPPORT + ld a,7 + ld ($fffe),a + call sms_detect_fm + endif + ld a,1 + ld ($fffe),a + endif if MEMOTECH Z80_CTC: equ $08 endif @@ -3486,7 +4288,7 @@ Z80_CTC: equ $28 ld a,$92 ; Setup 8255 for keyboard/joystick reading. out ($97),a endif - if SG1000 + if SG1000+SMS ; Contributed by SiRioKD ld a,$9F ; Turn off PSG out (PSG),a @@ -3499,7 +4301,7 @@ Z80_CTC: equ $28 ld a,$92 ; Setup 8255 for SC3000. out ($df),a endif - if SG1000+SVI + if SG1000+SMS+SVI ; Wait for VDP ready (around 1000 ms) ld b,11 ld de,$FFFF @@ -3511,6 +4313,9 @@ Z80_CTC: equ $28 djnz .delay1 else di + if COLECO_SPINNER + im 1 + endif ld sp,STACK endif if NABU @@ -3549,13 +4354,22 @@ Z80_CTC: equ $28 if COLECO ld a,($ffc0) ; Megacart endif - if SG1000 + if SG1000+SMS ld a,1 ; Sega mapper ld ($fffe),a endif if MSX + if KONAMI + ld a,1 + ld ($6000),a + inc a + ld ($8000),a + inc a + ld ($a000),a + else ld a,1 ; ASCII 16K ld ($7000),a + endif endif endif if MEMOTECH+EINSTEIN+NABU @@ -3566,16 +4380,36 @@ Z80_CTC: equ $28 ld (hl),0 ldir ld (lfsr),ix - else + endif + if MSX+SVI+SMS + ld ix,(lfsr) + if SMS + ld iy,(fm_slot) + endif + ld hl,BASE_RAM + ld de,BASE_RAM+1 + ld bc,RAM_SIZE-1 + ld (hl),0 + ldir + if SMS + ld (fm_slot),iy + endif + ld (lfsr),ix + endif + if COLECO+SG1000+SORD+PENCIL+PV2000 ld hl,(lfsr) ; Save RAM trash for random generator. ld de,BASE_RAM xor a ld (de),a inc de if PV2000 - bit 7,d - else - bit 2,d + bit 7,d ; 2.5K of RAM + endif + if PENCIL + bit 3,d ; 2K of RAM + endif + if COLECO+SG1000+SORD + bit 2,d ; 1K of RAM endif jp z,$-4 ld (lfsr),hl @@ -3663,7 +4497,7 @@ WRITE_VRAM: equ $1fdf jr nz,$+3 dec a endif - if SG1000+SVI+SORD+PV2000+NABU + if SG1000+SMS+SVI+SORD+PV2000+NABU ld a,1 ; Always NTSC. endif if MEMOTECH+EINSTEIN @@ -3673,9 +4507,32 @@ WRITE_VRAM: equ $1fdf call RSLREG ld b,1 ; $4000-$7fff call get_slot_mapping + ld (cartridge_slot),a ld h,$80 call ENASLT ; Map into $8000-$BFFF - + if FM_SUPPORT + ld a,$ff ; FM hasn't been discovered. + ld (fm_slot),a + if KONAMI + ld a,14 + ld ($8000),a + inc a + ld ($a000),a + else + ld a,7 + ld ($7000),a + endif + call msx_detect_fm + if KONAMI + ld a,2 + ld ($8000),a + inc a + ld ($a000),a + else + ld a,1 + ld ($7000),a + endif + endif ld a,($002b) cpl rlca @@ -3687,8 +4544,11 @@ WRITE_VRAM: equ $1fdf xor a ld (mode),a - + if SMS + call mode_4 + else call mode_0 + endif xor a ld (joy1_data),a @@ -3714,5 +4574,10 @@ WRITE_VRAM: equ $1fdf ld hl,nmi_handler ld ($7006),hl endif + if COLECO + if COLECO_SPINNER + ei + endif + endif ; CVBasic program start. diff --git a/driver.c b/driver.c index 52deec5..c61ab04 100644 --- a/driver.c +++ b/driver.c @@ -10,12 +10,11 @@ #include #include "cvbasic.h" #include "node.h" +#include "driver.h" #include "cpuz80.h" #include "cpu6502.h" #include "cpu9900.h" -static char driver_temp[MAX_LINE_SIZE]; - /* ** Dump peephole optimization */ @@ -117,6 +116,23 @@ void generic_jump(char *label) cpuz80_1op("JP", label); } +/* + ** Jump + */ +void generic_jump_short(char *label) +{ + if (target == CPU_6502) + cpu6502_1op("JMP", label); + if (target == CPU_9900) { + char temp[256]; + + sprintf(temp, "@%s", label); + cpu9900_1op("b", temp); + } + if (target == CPU_Z80) + cpuz80_1op("JR", label); +} + /* ** Jump if zero */ @@ -158,9 +174,18 @@ void generic_comparison_8bit(int min, int max, char *label) cpu6502_1op("BNE.L", label); } if (target == CPU_9900) { - sprintf(value, "%d", min * 256); - cpu9900_2op("ci", "r0", value); - cpu9900_1op("jne", label); + char internal_label[256], internal_label2[256]; + int number = next_local++; + + sprintf(internal_label, INTERNAL_PREFIX "%d", number); + sprintf(internal_label2, "@%s", label); + + sprintf(value, "%d ; %d*256", min * 256, min); + cpu9900_2op("li", "r1", value); + cpu9900_2op("cb", "r1", "r0"); + cpu9900_1op("jeq", internal_label); + cpu9900_1op("b", internal_label2); + cpu9900_label(internal_label); } return; } @@ -181,12 +206,24 @@ void generic_comparison_8bit(int min, int max, char *label) cpu6502_1op("BCS.L", label); } if (target == CPU_9900) { + char internal_label[256], internal_label2[256], internal_label3[256]; + int number; + + number = next_local++; + sprintf(internal_label, INTERNAL_PREFIX "%d", number); + sprintf(internal_label2, "@%s", label); + number = next_local++; + sprintf(internal_label3, INTERNAL_PREFIX "%d", number); + sprintf(value, "%d", min * 256); cpu9900_2op("ci", "r0", value); - cpu9900_1op("jl", label); - sprintf(value, "%d", max * 256); + cpu9900_1op("jl", internal_label3); + sprintf(value, "%d", max * 256 + 255); cpu9900_2op("ci", "r0", value); - cpu9900_1op("jh", label); + cpu9900_1op("jle", internal_label); + cpu9900_label(internal_label3); + cpu9900_1op("b", internal_label2); + cpu9900_label(internal_label); } } @@ -215,9 +252,17 @@ void generic_comparison_16bit(int min, int max, char *label) cpu6502_1op("BNE.L", label); } if (target == CPU_9900) { + char internal_label[256], internal_label2[256]; + int number = next_local++; + + sprintf(internal_label, INTERNAL_PREFIX "%d", number); + sprintf(internal_label2, "@%s", label); + sprintf(value, "%d", min); cpu9900_2op("ci", "r0", value); - cpu9900_1op("jne", label); + cpu9900_1op("jeq", internal_label); + cpu9900_1op("b", internal_label2); + cpu9900_label(internal_label); } return; } @@ -256,14 +301,57 @@ void generic_comparison_16bit(int min, int max, char *label) cpu6502_1op("BCS.L", label); } if (target == CPU_9900) { + char internal_label[256], internal_label2[256], internal_label3[256]; + int number; + + number = next_local++; + sprintf(internal_label, INTERNAL_PREFIX "%d", number); + sprintf(internal_label2, "@%s", label); + number = next_local++; + sprintf(internal_label3, INTERNAL_PREFIX "%d", number); + sprintf(value, "%d", min); cpu9900_2op("ci", "r0", value); - cpu9900_1op("jl", label); + cpu9900_1op("jl", internal_label3); sprintf(value, "%d", max); cpu9900_2op("ci", "r0", value); - cpu9900_1op("jh", label); + cpu9900_1op("jle", internal_label); + cpu9900_label(internal_label3); + cpu9900_1op("b", internal_label2); + cpu9900_label(internal_label); } } +/* + ** Generic disable interrupt + */ +void generic_interrupt_disable(void) +{ + if (consoles[machine].int_pin == 0) { + generic_call("nmi_off"); + } else { + if (target == CPU_Z80) + cpuz80_noop("DI"); + else if (target == CPU_6502) + cpu6502_noop("SEI"); + else if (target == CPU_9900) + cpu9900_1op("limi", "0"); + } +} - +/* + ** Generic enable interrupt + */ +void generic_interrupt_enable(void) +{ + if (consoles[machine].int_pin == 0) { + generic_call("nmi_on"); + } else { + if (target == CPU_Z80) + cpuz80_noop("EI"); + else if (target == CPU_6502) + cpu6502_noop("CLI"); + else if (target == CPU_9900) + cpu9900_1op("limi", "2"); + } +} diff --git a/driver.h b/driver.h index e0c4eb0..4d16252 100644 --- a/driver.h +++ b/driver.h @@ -13,6 +13,9 @@ extern void generic_label(char *); extern void generic_call(char *); extern void generic_return(void); extern void generic_jump(char *); +extern void generic_jump_short(char *); extern void generic_jump_zero(char *); extern void generic_comparison_8bit(int, int, char *); extern void generic_comparison_16bit(int, int, char *); +extern void generic_interrupt_disable(void); +extern void generic_interrupt_enable(void); diff --git a/examples/bank_nes.bas b/examples/bank_nes.bas new file mode 100644 index 0000000..a9a5bc4 --- /dev/null +++ b/examples/bank_nes.bas @@ -0,0 +1,1288 @@ + rem + rem Bank-switching example for NES/Famicom + rem by Oscar Toledo G. http://nanochess.org + rem Aug/26/2025 + rem + + BANK ROM 256 ' Select a 256KB target ROM size. + ' Now it is in bank 0. + + PLAY FULL + BANK SELECT 1 ' Select bank 1 to access. + PLAY tune_1 + + ' Display image. + PALETTE 0,$0F + PALETTE 1,$16 + PALETTE 2,$27 + PALETTE 3,$38 + SCREEN DISABLE + WAIT + + BANK SELECT 2 ' Select bank 2 to access. + + SCREEN image_pattern,0,132,12,16,12 + WAIT + SCREEN ENABLE + + GOSUB display_some_text + + WHILE 1: WEND + + BANK 1 ' Start of bank 1 data + + ' Bach Invention 8 (BWV779) + ' Fragment +tune_1: DATA BYTE 7 + MUSIC F4,- + MUSIC S,- + MUSIC A4,- + MUSIC S,- + MUSIC F4,- + MUSIC S,- + MUSIC C5,- + MUSIC S,- + MUSIC F4,- + MUSIC S,- + + MUSIC F5,- + MUSIC S,- + MUSIC E5,F3 + MUSIC D5,S + MUSIC C5,A3 + MUSIC D5,S + MUSIC C5,F3 + MUSIC A4#,S + MUSIC A4,C4 + MUSIC A4#,S + MUSIC A4,F3 + MUSIC G4,S + + MUSIC F4,F4 + MUSIC S,S + MUSIC A4,E4 + MUSIC S,D4 + MUSIC C5,C4 + MUSIC S,D4 + MUSIC A4,C4 + MUSIC S,A3# + MUSIC F5,A3 + MUSIC S,A3# + MUSIC C5,A3 + MUSIC S,G3 + + MUSIC A5,F3 + MUSIC C6,S + MUSIC A5#,A3 + MUSIC C6,S + MUSIC A5,C4 + MUSIC C6,S + MUSIC A5#,A3 + MUSIC C6,S + MUSIC A5,F4 + MUSIC C6,S + MUSIC A5#,C4 + MUSIC C6,S + + MUSIC F5,A3 + MUSIC A5,C4 + MUSIC G5,A3# + MUSIC A5,C4 + MUSIC F5,A3 + MUSIC A5,C4 + MUSIC G5,A3# + MUSIC A5,C4 + MUSIC F5,A3 + MUSIC A5,C4 + MUSIC G5,A3# + MUSIC A5,C4 + + MUSIC D5,F3 + MUSIC F5,A3 + MUSIC E5,G3 + MUSIC F5,A3 + MUSIC D5,F3 + MUSIC F5,A3 + MUSIC E5,G3 + MUSIC F5,A3 + MUSIC D5,F3 + MUSIC F5,A3 + MUSIC E5,G3 + MUSIC F5,A3 + + MUSIC B4,D3 + MUSIC S,F3 + MUSIC G4,E3 + MUSIC S,F3 + MUSIC D5,D3 + MUSIC S,F3 + MUSIC B4,E3 + MUSIC S,F3 + MUSIC F5,D3 + MUSIC S,F3 + MUSIC D5,E3 + MUSIC S,F3 + + MUSIC G5,B3 + MUSIC A5,S + MUSIC G5,G3 + MUSIC F5,S + MUSIC E5,C4 + MUSIC F5,S + MUSIC E5,G3 + MUSIC D5,S + MUSIC C5,E4 + MUSIC D5,S + MUSIC C5,C4 + MUSIC A4#,S + + MUSIC A4,F4 + MUSIC S,G4 + MUSIC D5,F4 + MUSIC C5,E4 + MUSIC B4,D4 + MUSIC C5,E4 + MUSIC B4,D4 + MUSIC A4,C4 + MUSIC G4,B3 + MUSIC A4,C4 + MUSIC G4,B3 + MUSIC F4,A3 + + MUSIC E4,G3 + MUSIC F4,S + MUSIC E4,C4 + MUSIC D4,B3 + MUSIC C4,A3 + MUSIC S,B3 + MUSIC C5,A3 + MUSIC B4,G3 + MUSIC C5,F3 + MUSIC S,G3 + MUSIC E4,F3 + MUSIC S,E3 + + MUSIC F4,D3 + MUSIC S,E3 + MUSIC C5,D3 + MUSIC S,C3 + MUSIC E4,G3 + MUSIC S,F3 + MUSIC C5,E3 + MUSIC S,F3 + MUSIC D4,G3 + MUSIC S,S + MUSIC B4,G2 + MUSIC S,S + + MUSIC C5,C4 + MUSIC S,S + MUSIC S,S + MUSIC S,S + MUSIC STOP + + BANK 2 + +display_some_text: PROCEDURE + + PRINT AT $01d1,"Hi!" + + PRINT AT $0211,"This is a bank-" + + PRINT AT $0231,"switching demo." + + PRINT AT $0271,":)" + + END + + ' CHRROM is technically outside of the ROM, but for + ' the purposes of UNROM 512 bank-switching it is saved + ' in reserved banks and copied to CHRRAM on boot. + + ' TMSColor 3.2 Aug/26/2025 + ' Command: ./tmscolor -nes -p1 -t128 -b yo.png yo.bas + ' Created: Tue Aug 26 10:07:21 2025 + + ' Start tile = 128. Total_tiles = 118 + CHRROM 0 PATTERN 128 + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "12222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222233" + BITMAP "22223333" + BITMAP "22333333" + + BITMAP "22222223" + BITMAP "22222333" + BITMAP "23223333" + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222223" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22223333" + BITMAP "22233333" + BITMAP "22333333" + + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22223333" + + BITMAP "22333333" + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "22223333" + BITMAP "22233333" + BITMAP "22233333" + BITMAP "22333333" + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333322" + BITMAP "33332222" + BITMAP "33322211" + BITMAP "33222111" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33323333" + BITMAP "22222222" + BITMAP "11111111" + BITMAP "11100111" + BITMAP "11111111" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "22333333" + BITMAP "11112333" + BITMAP "11111223" + + BITMAP "22222222" + BITMAP "22222223" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22223333" + BITMAP "22223333" + BITMAP "22233333" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333332" + BITMAP "33333332" + BITMAP "33333322" + BITMAP "33333322" + + BITMAP "33221111" + BITMAP "32211111" + BITMAP "22211111" + BITMAP "22111111" + BITMAP "21111111" + BITMAP "21111112" + BITMAP "11112222" + BITMAP "11122222" + + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "22222221" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "11111112" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "22111111" + BITMAP "22221111" + + BITMAP "22333333" + BITMAP "11122333" + BITMAP "11000233" + BITMAP "11100023" + BITMAP "11100001" + BITMAP "11100000" + BITMAP "11100000" + BITMAP "11110000" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "13333333" + BITMAP "01333333" + BITMAP "00233333" + + BITMAP "22233333" + BITMAP "22333333" + BITMAP "22333333" + BITMAP "22333333" + BITMAP "23333333" + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "33333321" + BITMAP "33333321" + BITMAP "33333321" + BITMAP "33333212" + BITMAP "33333212" + BITMAP "33333222" + BITMAP "33333222" + BITMAP "33333222" + + BITMAP "11222222" + BITMAP "12222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "22221111" + BITMAP "22222111" + BITMAP "22222111" + BITMAP "22222211" + BITMAP "22222221" + BITMAP "22222221" + BITMAP "22222221" + BITMAP "22222221" + + BITMAP "11110000" + BITMAP "11110000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111100" + + BITMAP "00033333" + BITMAP "00023333" + BITMAP "00003333" + BITMAP "00002333" + BITMAP "00002333" + BITMAP "00001333" + BITMAP "00000333" + BITMAP "00000233" + + BITMAP "22222222" + BITMAP "22222223" + BITMAP "22222223" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22222333" + + BITMAP "33332122" + BITMAP "33332122" + BITMAP "33332122" + BITMAP "33332222" + BITMAP "33333122" + BITMAP "33333122" + BITMAP "33332111" + BITMAP "33332111" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222232" + BITMAP "22232233" + BITMAP "22222222" + BITMAP "11122222" + BITMAP "11112222" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22233333" + BITMAP "22233222" + BITMAP "22222111" + BITMAP "22221110" + BITMAP "22221112" + + BITMAP "22222221" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "32222111" + BITMAP "22221111" + BITMAP "01111111" + BITMAP "11222211" + BITMAP "33333222" + + BITMAP "11111100" + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11110000" + BITMAP "11110001" + + BITMAP "00000233" + BITMAP "00000232" + BITMAP "00000132" + BITMAP "00000122" + BITMAP "01110122" + BITMAP "11000112" + BITMAP "00001111" + BITMAP "00001122" + + BITMAP "33333333" + BITMAP "22233333" + BITMAP "22222233" + BITMAP "22222223" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "23333333" + BITMAP "23333333" + + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22223333" + + BITMAP "33332111" + BITMAP "33222122" + BITMAP "32321222" + BITMAP "32101211" + BITMAP "10233110" + BITMAP "12333111" + BITMAP "33333112" + BITMAP "33233122" + + BITMAP "11111122" + BITMAP "22222112" + BITMAP "22212111" + BITMAP "11110111" + BITMAP "22000111" + BITMAP "22111101" + BITMAP "22222211" + BITMAP "22222212" + + BITMAP "22211222" + BITMAP "22211221" + BITMAP "22111211" + BITMAP "22111121" + BITMAP "22111122" + BITMAP "22111122" + BITMAP "22211122" + BITMAP "22211122" + + BITMAP "33332112" + BITMAP "12231001" + BITMAP "23110110" + BITMAP "22211221" + BITMAP "21222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "20011111" + BITMAP "11111111" + BITMAP "11222111" + BITMAP "11222111" + BITMAP "21222211" + BITMAP "21222211" + BITMAP "21222211" + BITMAP "12222111" + + BITMAP "10000222" + BITMAP "10001221" + BITMAP "10000221" + BITMAP "10001121" + BITMAP "11001121" + BITMAP "11111121" + BITMAP "11111121" + BITMAP "11101111" + + BITMAP "23333333" + BITMAP "23333333" + BITMAP "22333333" + BITMAP "22333333" + BITMAP "22333333" + BITMAP "22233333" + BITMAP "22233333" + BITMAP "22223333" + + BITMAP "22223333" + BITMAP "22223333" + BITMAP "22222333" + BITMAP "22223333" + BITMAP "22223333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22223333" + + BITMAP "33232122" + BITMAP "33322122" + BITMAP "33332122" + BITMAP "33333211" + BITMAP "33333222" + BITMAP "33333322" + BITMAP "33333322" + BITMAP "33333322" + + BITMAP "22222112" + BITMAP "22222122" + BITMAP "22221122" + BITMAP "12122222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "22221112" + BITMAP "22211111" + BITMAP "22211112" + BITMAP "22211111" + BITMAP "22211111" + BITMAP "22221211" + BITMAP "32222221" + BITMAP "22111111" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222232" + BITMAP "22223332" + BITMAP "12222332" + BITMAP "12222222" + BITMAP "12222222" + BITMAP "22222222" + + BITMAP "12222111" + BITMAP "22222111" + BITMAP "22222111" + BITMAP "22222111" + BITMAP "22222111" + BITMAP "22222111" + BITMAP "22221111" + BITMAP "22221111" + + BITMAP "11101211" + BITMAP "11112222" + BITMAP "11111112" + BITMAP "11111222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + + BITMAP "22223333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222223" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "22333333" + + BITMAP "22223333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22222333" + + BITMAP "33333332" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "22222222" + BITMAP "22222212" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "32222222" + BITMAP "32222111" + BITMAP "33222211" + BITMAP "33222222" + + BITMAP "22112222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222211" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11222222" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22111222" + BITMAP "11112222" + BITMAP "11222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "22221211" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222111" + + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11101222" + + BITMAP "22233333" + BITMAP "22223333" + BITMAP "22222233" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "22333333" + BITMAP "22223333" + BITMAP "22222333" + BITMAP "22222233" + + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222233" + + BITMAP "33322222" + BITMAP "33322222" + BITMAP "33332222" + BITMAP "33332122" + BITMAP "33333222" + BITMAP "33333212" + BITMAP "33333322" + BITMAP "33333332" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222112" + BITMAP "22221112" + BITMAP "22211111" + BITMAP "22211111" + + BITMAP "22222111" + BITMAP "22221111" + BITMAP "22211111" + BITMAP "22111111" + BITMAP "21111111" + BITMAP "11111112" + BITMAP "11211132" + BITMAP "11112321" + + BITMAP "11001222" + BITMAP "11001222" + BITMAP "10101222" + BITMAP "11102212" + BITMAP "21112112" + BITMAP "11022022" + BITMAP "01132022" + BITMAP "10231132" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "12222222" + BITMAP "00122222" + + BITMAP "22222333" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222233" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333321" + BITMAP "33333100" + BITMAP "33321010" + + BITMAP "33333332" + BITMAP "33333211" + BITMAP "33321002" + BITMAP "32100112" + BITMAP "20011122" + BITMAP "00111123" + BITMAP "10110232" + BITMAP "01111322" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22111111" + BITMAP "22111111" + BITMAP "22122222" + BITMAP "22112222" + BITMAP "21012222" + BITMAP "21012222" + + BITMAP "22111111" + BITMAP "21111111" + BITMAP "11111222" + BITMAP "12222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222223" + BITMAP "22222233" + + BITMAP "11223311" + BITMAP "22223201" + BITMAP "22232110" + BITMAP "22331102" + BITMAP "23321113" + BITMAP "33211023" + BITMAP "33110233" + BITMAP "32011332" + + BITMAP "02320231" + BITMAP "13311320" + BITMAP "23202310" + BITMAP "33013201" + BITMAP "31033101" + BITMAP "20232010" + BITMAP "11331010" + BITMAP "02320110" + + BITMAP "00012222" + BITMAP "00012222" + BITMAP "00000112" + BITMAP "00000001" + BITMAP "00000000" + BITMAP "11000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "12222222" + BITMAP "01222222" + BITMAP "00122222" + BITMAP "00012222" + BITMAP "00000112" + + BITMAP "22222223" + BITMAP "22222223" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333331" + BITMAP "33333210" + BITMAP "23333111" + BITMAP "22232111" + BITMAP "22331110" + + BITMAP "33200100" + BITMAP "32011001" + BITMAP "10110011" + BITMAP "01110011" + BITMAP "11100111" + BITMAP "11101110" + BITMAP "01011111" + BITMAP "11111101" + + BITMAP "11102322" + BITMAP "11112322" + BITMAP "01123322" + BITMAP "11123222" + BITMAP "11132222" + BITMAP "21022222" + BITMAP "21122222" + BITMAP "20022122" + + BITMAP "21012222" + BITMAP "21012222" + BITMAP "21011222" + BITMAP "21011222" + BITMAP "21011222" + BITMAP "11011222" + BITMAP "22011222" + BITMAP "22101232" + + BITMAP "22222333" + BITMAP "22223333" + BITMAP "22233332" + BITMAP "22333331" + BITMAP "23333311" + BITMAP "22223211" + BITMAP "22222110" + BITMAP "22221112" + + BITMAP "21102320" + BITMAP "11023310" + BITMAP "01133202" + BITMAP "10232013" + BITMAP "02331023" + BITMAP "13320233" + BITMAP "23301331" + BITMAP "33102320" + + BITMAP "13201001" + BITMAP "33101000" + BITMAP "32011000" + BITMAP "31010000" + BITMAP "20100010" + BITMAP "01100110" + BITMAP "01000000" + BITMAP "11001100" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "00000001" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "22222222" + BITMAP "01222222" + BITMAP "00122222" + BITMAP "00012222" + BITMAP "00001222" + BITMAP "00000122" + BITMAP "00000112" + BITMAP "00000000" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "11222222" + + BITMAP "22321110" + BITMAP "22321100" + BITMAP "22321100" + BITMAP "22311100" + BITMAP "23311101" + BITMAP "23310101" + BITMAP "23310111" + BITMAP "23320111" + + BITMAP "10111102" + BITMAP "01111112" + BITMAP "11111112" + BITMAP "11111112" + BITMAP "11111022" + BITMAP "11111121" + BITMAP "11111121" + BITMAP "11111111" + + BITMAP "20023112" + BITMAP "10013212" + BITMAP "10001222" + BITMAP "00001222" + BITMAP "00001222" + BITMAP "00001222" + BITMAP "00012223" + BITMAP "00012233" + + BITMAP "22201322" + BITMAP "22201322" + BITMAP "22222223" + BITMAP "22222333" + BITMAP "22222332" + BITMAP "32222322" + BITMAP "33212221" + BITMAP "33202101" + + BITMAP "22221113" + BITMAP "22211023" + BITMAP "22111233" + BITMAP "21111332" + BITMAP "11103331" + BITMAP "11023310" + BITMAP "10233201" + BITMAP "02332013" + + BITMAP "32013310" + BITMAP "31133201" + BITMAP "20233011" + BITMAP "01331011" + BITMAP "03320111" + BITMAP "23311110" + BITMAP "33211000" + BITMAP "32111111" + + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11110000" + BITMAP "11000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000001" + BITMAP "00000001" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "11000000" + BITMAP "11000000" + BITMAP "11100000" + BITMAP "11100000" + + BITMAP "00012222" + BITMAP "00000122" + BITMAP "00000002" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "23310111" + BITMAP "23301111" + BITMAP "23201111" + BITMAP "23111111" + BITMAP "22111111" + BITMAP "22011111" + BITMAP "21111111" + BITMAP "21111111" + + BITMAP "11111111" + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11101110" + BITMAP "11101110" + BITMAP "11011100" + BITMAP "11011100" + BITMAP "10011100" + + BITMAP "00012333" + BITMAP "00123333" + BITMAP "00123333" + BITMAP "01223333" + BITMAP "01223333" + BITMAP "01233333" + BITMAP "12233333" + BITMAP "12333332" + + BITMAP "33201110" + BITMAP "33201102" + BITMAP "33111123" + BITMAP "32111122" + BITMAP "21111100" + BITMAP "21111011" + BITMAP "11123201" + BITMAP "11133311" + + BITMAP "23331122" + BITMAP "33201110" + BITMAP "32111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "10111111" + BITMAP "11111111" + BITMAP "11101111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "10000001" + BITMAP "11100001" + BITMAP "11111001" + BITMAP "11111001" + BITMAP "11111001" + BITMAP "11111011" + BITMAP "11110011" + BITMAP "11100011" + + BITMAP "11110000" + BITMAP "11110000" + BITMAP "11110000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111100" + BITMAP "11111100" + + BITMAP "00000000" + BITMAP "00000001" + BITMAP "00000012" + BITMAP "00000222" + BITMAP "00002322" + BITMAP "00013332" + BITMAP "00023333" + BITMAP "00133333" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222221" + BITMAP "22222221" + BITMAP "22222210" + BITMAP "22222211" + + BITMAP "21111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111110" + BITMAP "11111110" + + BITMAP "10111001" + BITMAP "11111001" + BITMAP "11111012" + BITMAP "11110012" + BITMAP "11110122" + BITMAP "11110122" + BITMAP "11101222" + BITMAP "11001123" + + BITMAP "23333222" + BITMAP "23332232" + BITMAP "23223331" + BITMAP "32233321" + BITMAP "22333321" + BITMAP "23333321" + BITMAP "33332211" + BITMAP "33332211" + + BITMAP "11133311" + BITMAP "11112101" + BITMAP "11110111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11110111" + BITMAP "11100111" + BITMAP "11101111" + BITMAP "11001111" + BITMAP "10011111" + + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "11100011" + BITMAP "11000111" + BITMAP "11000111" + BITMAP "11000111" + BITMAP "11000111" + BITMAP "11001111" + BITMAP "10001111" + BITMAP "10001111" + + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "10000000" + BITMAP "10000000" + BITMAP "10000000" + BITMAP "11000000" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00100000" + BITMAP "00100001" + BITMAP "00101102" + + BITMAP "00233333" + BITMAP "01333323" + BITMAP "02333323" + BITMAP "13333223" + BITMAP "23333223" + BITMAP "23333223" + BITMAP "33332233" + BITMAP "33332233" + + + ' Width = 12, height = 16 +image_pattern: + DATA BYTE $80,$81,$81,$81,$82,$83,$84,$84,$84,$84,$84,$84 + DATA BYTE $81,$81,$81,$85,$86,$84,$84,$84,$84,$84,$84,$84 + DATA BYTE $81,$81,$87,$88,$84,$84,$84,$84,$84,$84,$84,$84 + DATA BYTE $81,$81,$89,$8a,$8b,$8c,$84,$84,$84,$84,$84,$84 + DATA BYTE $81,$8d,$8e,$8f,$90,$91,$92,$93,$84,$84,$84,$84 + DATA BYTE $81,$94,$95,$96,$81,$97,$98,$99,$84,$84,$84,$84 + DATA BYTE $9a,$84,$9b,$9c,$9d,$9e,$9f,$a0,$a1,$a2,$84,$84 + DATA BYTE $a3,$84,$a4,$a5,$a6,$a7,$a8,$a9,$81,$aa,$84,$84 + DATA BYTE $ab,$84,$ac,$ad,$ae,$af,$b0,$b1,$81,$b2,$b3,$84 + DATA BYTE $b4,$84,$b5,$b6,$b7,$b8,$b9,$ba,$81,$81,$bb,$bc + DATA BYTE $bd,$84,$84,$be,$81,$bf,$c0,$c1,$c2,$81,$81,$81 + DATA BYTE $c3,$84,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$81,$81 + DATA BYTE $cc,$cd,$ce,$cf,$d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7 + DATA BYTE $81,$d8,$d9,$da,$db,$dc,$dd,$de,$df,$d4,$d4,$e0 + DATA BYTE $81,$e1,$e2,$e3,$e4,$e5,$e6,$e7,$e8,$d4,$d4,$e9 + DATA BYTE $ea,$eb,$ec,$ed,$ee,$ef,$f0,$f1,$f2,$f3,$f4,$f5 diff --git a/examples/bank_sms.bas b/examples/bank_sms.bas new file mode 100644 index 0000000..59923a3 --- /dev/null +++ b/examples/bank_sms.bas @@ -0,0 +1,554 @@ + rem + rem Bank-switching example + rem by Oscar Toledo G. http://nanochess.org + rem Apr/27/2024 + rem Apr/27/2025. Adapted for Sega Master System. + rem + + BANK ROM 128 ' Select a 128KB target ROM size. + ' Now it is in bank 0. + + PLAY FULL + BANK SELECT 1 ' Select bank 1 to access. + PLAY tune_2 + + BANK SELECT 2 ' Select bank 2 to access. + + ' Notice bank 2 is selected, but the + ' music is being played from bank 1. + + DEFINE CHAR PLETTER 64,185,image_char + PALETTE 9,$0B + PALETTE 11,$1B + PALETTE 14,$2B + SCREEN image_pattern,0,200,16,12,16 + + WHILE 1: WEND + + BANK 1 ' Start of bank 1 data + + ' Mecha-8 level 5: alone + ' Fragment +tune_2: DATA BYTE 5 + MUSIC G5#Y,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC F5#,G3#,-,M2 + MUSIC S,S,-,M2 + MUSIC E5,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC D5#,G3#,-,M2 + MUSIC S,S,-,M2 + MUSIC E5,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC F5#,G3#,-,M2 + MUSIC S,S,-,M2 + MUSIC G5#,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC S,G3#,-,M2 + MUSIC S,S,-,M2 + MUSIC S,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC C5#,G3#,-,M2 + MUSIC -,S,-,M2 + MUSIC G5#,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC E5,G3#,-,M2 + MUSIC -,S,-,M2 + MUSIC F5#,B2,-,M1 + MUSIC S,S,-,M2 + MUSIC S,F3#,-,M2 + MUSIC S,S,-,M2 + MUSIC S,B2,-,M1 + MUSIC S,S,-,M2 + MUSIC -,F3#,-,M2 + MUSIC -,S,-,M2 + MUSIC -,B2,-,M1 + MUSIC -,S,-,M2 + MUSIC -,F3#,-,M2 + MUSIC -,S,-,M2 + MUSIC -,B2,-,M1 + MUSIC -,S,-,M2 + MUSIC -,F3#,-,M1 + MUSIC -,S,-,M2 + MUSIC C5#,B2,-,M1 + MUSIC S,S,-,M2 + MUSIC F5#,F3#,-,M1 + MUSIC S,S,-,M2 + MUSIC C5#,B2,-,M1 + MUSIC S,S,-,M2 + MUSIC E5,A2,-,M1 + MUSIC S,S,-,M2 + MUSIC S,E3,-,M1 + MUSIC S,S,-,M2 + MUSIC S,A2,-,M1 + MUSIC S,S,-,M2 + MUSIC S,E3,-,M1 + MUSIC S,S,-,M2 + MUSIC S,A2,-,M1 + MUSIC S,S,-,M2 + MUSIC S,E3,-,M1 + MUSIC S,S,-,M2 + MUSIC S,A2,-,M1 + MUSIC S,S,-,M2 + MUSIC F5#,E3,-,M1 + MUSIC S,S,-,M2 + MUSIC E5,A2,-,M2 + MUSIC S,S,-,M2 + MUSIC D5#,E3,-,M1 + MUSIC S,S,-,M2 + MUSIC S,A2,-,M2 + MUSIC S,S,-,M2 + MUSIC C5,G2#,-,M1 + MUSIC S,S,-,M2 + MUSIC S,D3#,-,M2 + MUSIC S,S,-,M2 + MUSIC S,G2#,-,M1 + MUSIC S,S,-,M2 + MUSIC S,D3#,-,M2 + MUSIC S,S,-,M2 + MUSIC S,G2#,-,M1 + MUSIC S,S,-,M2 + MUSIC -,D3#,-,M2 + MUSIC -,S,-,M2 + MUSIC -,G2#,-,M1 + MUSIC -,S,-,M2 + MUSIC -,D3#,-,M2 + MUSIC -,S,-,M2 + MUSIC -,G2#,-,M1 + MUSIC -,S,-,M2 + MUSIC -,D3#,-,M1 + MUSIC -,S,-,M3 + MUSIC -,G2#,-,M1 + MUSIC -,S,-,M2 + MUSIC -,D3#,-,M1 + MUSIC -,S,-,M3 + MUSIC -,G2#,-,M1 + MUSIC -,S,-,M1 + MUSIC -,D3#,-,M1 + MUSIC -,S,-,M1 + MUSIC REPEAT + + BANK 2 ' Start of bank 2 data. + + ' Start tile = 64. Total_tiles = 185 +image_char: + DATA BYTE $69,$b4,$ff,$00,$24,$8f,$03,$00 + DATA BYTE $e4,$03,$7f,$00,$00,$41,$1f,$00 + DATA BYTE $01,$ff,$fe,$ff,$dd,$03,$1f,$ba + DATA BYTE $61,$03,$00,$80,$80,$00,$02,$c0 + DATA BYTE $c0,$00,$c0,$40,$f0,$b0,$17,$f0 + DATA BYTE $00,$f0,$00,$01,$03,$04,$f0,$f4 + DATA BYTE $f4,$00,$f4,$33,$00,$79,$23,$de + DATA BYTE $03,$27,$80,$f9,$00,$80,$6f,$07 + DATA BYTE $22,$03,$f7,$00,$80,$f3,$00,$26 + DATA BYTE $80,$fb,$00,$34,$69,$0f,$03,$81 + DATA BYTE $8f,$a2,$03,$bf,$00,$f9,$13,$04 + DATA BYTE $8f,$03,$2a,$c7,$03,$ab,$e3,$03 + DATA BYTE $2f,$71,$03,$4f,$ae,$6a,$03,$f3 + DATA BYTE $00,$fe,$ec,$03,$00,$6e,$fc,$11 + DATA BYTE $03,$f8,$fd,$00,$00,$3f,$71,$00 + DATA BYTE $03,$04,$3b,$9c,$07,$b3,$29,$7b + DATA BYTE $00,$00,$de,$17,$01,$03,$0f,$b3 + DATA BYTE $b3,$bf,$07,$3f,$33,$1c,$03,$1b + DATA BYTE $64,$03,$7d,$03,$ff,$10,$fd,$ff + DATA BYTE $c0,$2e,$f9,$a0,$70,$30,$70,$f0 + DATA BYTE $a3,$60,$03,$ee,$ee,$e8,$00,$0e + DATA BYTE $e2,$e2,$e0,$80,$03,$44,$ec,$00 + DATA BYTE $80,$cc,$00,$00,$84,$96,$f8,$00 + DATA BYTE $5b,$f1,$00,$a0,$f3,$e6,$7b,$a2 + DATA BYTE $0b,$0f,$00,$20,$00,$07,$00,$80 + DATA BYTE $0f,$8f,$0f,$e0,$03,$a0,$1f,$bf + DATA BYTE $3f,$20,$b0,$0f,$03,$a0,$0f,$af + DATA BYTE $2f,$ed,$03,$51,$3b,$cf,$11,$00 + DATA BYTE $00,$c7,$00,$10,$c1,$18,$00,$04 + DATA BYTE $c0,$b5,$48,$f5,$00,$00,$fc,$bb + DATA BYTE $00,$07,$bb,$6f,$07,$8b,$b7,$bb + DATA BYTE $0b,$03,$e2,$a7,$61,$69,$03,$63 + DATA BYTE $e7,$30,$00,$61,$97,$98,$60,$17 + DATA BYTE $44,$40,$e8,$a7,$06,$41,$ec,$b8 + DATA BYTE $f1,$b3,$1a,$e8,$97,$c4,$bf,$cc + DATA BYTE $44,$83,$63,$66,$99,$07,$02,$46 + DATA BYTE $1d,$bf,$24,$c2,$cb,$77,$30,$03 + DATA BYTE $17,$bb,$c5,$cb,$e1,$f3,$bc,$74 + DATA BYTE $a3,$27,$aa,$85,$bb,$cf,$07,$ae + DATA BYTE $ce,$03,$a3,$27,$6f,$84,$87,$ef + DATA BYTE $f1,$0b,$68,$8f,$03,$48,$35,$07 + DATA BYTE $e6,$00,$7f,$e7,$03,$17,$a3,$31 + DATA BYTE $03,$f4,$0b,$e0,$8f,$03,$e4,$39 + DATA BYTE $03,$0f,$f2,$c2,$8f,$eb,$98,$00 + DATA BYTE $00,$e7,$64,$00,$ef,$00,$00,$5a + DATA BYTE $ed,$00,$2f,$03,$af,$00,$84,$af + DATA BYTE $20,$f0,$03,$d0,$f0,$20,$e1,$c1 + DATA BYTE $e0,$bb,$03,$0b,$8b,$03,$00,$e8 + DATA BYTE $00,$12,$f4,$f5,$4c,$bf,$12,$40 + DATA BYTE $e3,$00,$60,$00,$97,$b5,$80,$84 + DATA BYTE $e3,$fd,$02,$00,$7d,$7d,$7b,$07 + DATA BYTE $fb,$f9,$7f,$07,$1e,$ff,$fa,$ef + DATA BYTE $82,$fb,$df,$b7,$00,$18,$0f,$fe + DATA BYTE $9f,$6f,$f9,$8b,$a7,$88,$b7,$80 + DATA BYTE $2f,$88,$07,$30,$0f,$c7,$f8,$cf + DATA BYTE $83,$00,$03,$23,$23,$95,$db,$02 + DATA BYTE $c5,$a3,$bd,$03,$84,$b7,$20,$00 + DATA BYTE $c1,$e1,$e1,$60,$80,$e0,$e0,$40 + DATA BYTE $04,$08,$48,$48,$00,$78,$00,$00 + DATA BYTE $44,$79,$00,$80,$79,$b0,$7a,$2d + DATA BYTE $0b,$7d,$00,$11,$87,$7e,$69,$00 + DATA BYTE $0b,$c6,$ab,$ee,$98,$00,$00,$a7 + DATA BYTE $04,$41,$a7,$a7,$00,$27,$97,$b3 + DATA BYTE $b7,$17,$01,$af,$a8,$b7,$27,$ff + DATA BYTE $f9,$f7,$10,$87,$fb,$ff,$0b,$46 + DATA BYTE $0c,$bf,$0c,$c0,$8f,$22,$06,$8f + DATA BYTE $00,$00,$87,$00,$01,$c0,$fb,$bb + DATA BYTE $d3,$f0,$fe,$0e,$07,$e3,$83,$cb + DATA BYTE $c4,$a3,$64,$c7,$03,$40,$9d,$03 + DATA BYTE $17,$c9,$f3,$c2,$af,$f0,$c7,$0b + DATA BYTE $a8,$85,$b7,$fd,$03,$f3,$8f,$3c + DATA BYTE $78,$03,$26,$7a,$fd,$03,$b8,$1b + DATA BYTE $07,$bc,$ff,$30,$07,$b8,$93,$99 + DATA BYTE $98,$03,$bc,$ab,$4c,$3e,$03,$63 + DATA BYTE $1e,$03,$5f,$1e,$03,$1c,$7f,$03 + DATA BYTE $17,$d7,$ce,$00,$3f,$03,$ff,$9b + DATA BYTE $af,$c2,$f3,$63,$c0,$03,$88,$1c + DATA BYTE $03,$08,$e7,$5e,$03,$c3,$12,$e0 + DATA BYTE $cb,$c7,$cf,$f4,$d7,$5e,$03,$5a + DATA BYTE $fa,$00,$72,$03,$97,$a2,$81,$00 + DATA BYTE $00,$89,$00,$2c,$00,$ad,$00,$23 + DATA BYTE $07,$ce,$01,$de,$de,$df,$d3,$93 + DATA BYTE $d8,$03,$e3,$46,$ff,$03,$70,$07 + DATA BYTE $03,$83,$7f,$7c,$22,$ff,$c2,$f7 + DATA BYTE $00,$fb,$c0,$f1,$b1,$f0,$c0,$f8 + DATA BYTE $b8,$3a,$f8,$c0,$8f,$78,$9f,$e5 + DATA BYTE $ee,$00,$c4,$00,$d8,$d8,$18,$00 + DATA BYTE $f8,$f8,$64,$20,$9b,$b0,$40,$63 + DATA BYTE $e0,$0c,$00,$3f,$31,$2f,$0c,$6f + DATA BYTE $61,$6f,$00,$4f,$01,$00,$00,$93 + DATA BYTE $00,$40,$90,$b7,$00,$fb,$00,$8b + DATA BYTE $e3,$b8,$03,$fb,$96,$30,$00,$88 + DATA BYTE $4f,$03,$00,$01,$03,$11,$02,$03 + DATA BYTE $05,$03,$07,$31,$80,$d7,$21,$dc + DATA BYTE $83,$48,$8f,$5e,$1f,$8f,$03,$f8 + DATA BYTE $07,$a3,$7e,$df,$a6,$3c,$31,$03 + DATA BYTE $0e,$03,$03,$ae,$20,$1b,$07,$cf + DATA BYTE $d0,$e3,$98,$0a,$03,$02,$c6,$03 + DATA BYTE $82,$f1,$a3,$f3,$8b,$c9,$86,$16 + DATA BYTE $e7,$0b,$cf,$9c,$37,$0b,$e6,$03 + DATA BYTE $1f,$1b,$03,$9f,$7f,$37,$07,$0f + DATA BYTE $1d,$03,$07,$73,$f8,$e7,$4f,$3f + DATA BYTE $e3,$8f,$fc,$eb,$2e,$cb,$06,$f0 + DATA BYTE $e3,$0f,$fc,$7a,$a7,$77,$e9,$c7 + DATA BYTE $e6,$cb,$4a,$e1,$00,$01,$a2,$1c + DATA BYTE $f5,$b5,$21,$01,$67,$66,$67,$b2 + DATA BYTE $ff,$dd,$00,$ff,$80,$10,$6f,$6f + DATA BYTE $7f,$18,$c7,$f9,$56,$80,$51,$03 + DATA BYTE $00,$2f,$18,$fe,$e6,$fc,$08,$fe + DATA BYTE $f6,$09,$fe,$08,$e6,$e6,$d3,$57 + DATA BYTE $ce,$00,$16,$e3,$84,$e7,$b8,$0b + DATA BYTE $b8,$38,$00,$18,$00,$f8,$03,$0e + DATA BYTE $2f,$54,$21,$83,$2f,$70,$2f,$03 + DATA BYTE $27,$00,$37,$8b,$00,$00,$b7,$00 + DATA BYTE $45,$07,$27,$00,$a5,$bb,$6f,$66 + DATA BYTE $00,$c7,$82,$e0,$f3,$01,$f3,$f1 + DATA BYTE $a9,$43,$f7,$97,$01,$a1,$c0,$81 + DATA BYTE $f1,$aa,$12,$ef,$e0,$00,$60,$60 + DATA BYTE $71,$f0,$f8,$e0,$00,$f8,$70,$fe + DATA BYTE $ee,$fe,$03,$07,$04,$80,$ac,$0f + DATA BYTE $08,$0e,$07,$05,$0f,$0a,$4d,$00 + DATA BYTE $0f,$07,$1f,$18,$0f,$05,$1f,$1a + DATA BYTE $0e,$0f,$0d,$17,$12,$03,$7f,$1f + DATA BYTE $17,$ef,$7d,$53,$03,$13,$ab,$bf + DATA BYTE $d6,$9b,$1d,$c2,$e3,$5e,$0f,$6d + DATA BYTE $89,$03,$05,$9a,$74,$1b,$43,$01 + DATA BYTE $8e,$00,$47,$3b,$3b,$fb,$f0,$0d + DATA BYTE $0d,$f9,$ca,$03,$e7,$40,$04,$04 + DATA BYTE $fc,$f0,$0c,$0c,$70,$f0,$03,$20 + DATA BYTE $dc,$dc,$80,$a8,$1c,$1c,$7c,$e0 + DATA BYTE $e7,$4f,$bc,$18,$07,$1a,$87,$f0 + DATA BYTE $c3,$ca,$00,$10,$0b,$07,$f0,$e3 + DATA BYTE $9b,$01,$90,$6f,$f9,$63,$ab,$22 + DATA BYTE $77,$03,$98,$67,$03,$35,$10,$ef + DATA BYTE $89,$e7,$3f,$ae,$73,$07,$03,$f4 + DATA BYTE $1c,$4b,$f4,$eb,$5d,$0b,$b8,$03 + DATA BYTE $f0,$e4,$d7,$cf,$1b,$9b,$d3,$e2 + DATA BYTE $ef,$72,$02,$ab,$3c,$3f,$ab,$ee + DATA BYTE $03,$ee,$0b,$83,$75,$bc,$00,$14 + DATA BYTE $c3,$00,$00,$5c,$a0,$00,$e3,$3a + DATA BYTE $4a,$eb,$46,$00,$e2,$e3,$c9,$bf + DATA BYTE $16,$e6,$00,$66,$00,$88,$03,$2e + DATA BYTE $00,$00,$3e,$b8,$00,$bb,$98,$01 + DATA BYTE $9f,$65,$00,$08,$00,$a1,$eb,$7c + DATA BYTE $00,$60,$b8,$00,$cf,$c3,$c7,$ef + DATA BYTE $e8,$ef,$9c,$f9,$0c,$1f,$45,$13 + DATA BYTE $95,$38,$26,$0f,$2f,$04,$0f,$00 + DATA BYTE $0b,$0f,$24,$7e,$5a,$2e,$e4,$fe + DATA BYTE $38,$1a,$e6,$03,$e0,$f6,$18,$16 + DATA BYTE $e6,$f4,$c5,$00,$b7,$fc,$7f,$fc + DATA BYTE $97,$8c,$03,$fe,$82,$d9,$06,$df + DATA BYTE $3e,$0f,$cf,$a3,$2c,$f1,$a6,$10 + DATA BYTE $13,$e3,$11,$11,$03,$05,$87,$16 + DATA BYTE $e4,$11,$f2,$81,$f0,$19,$64,$64 + DATA BYTE $18,$03,$0d,$d7,$03,$8c,$77,$02 + DATA BYTE $76,$00,$12,$2f,$2d,$1f,$b1,$0e + DATA BYTE $0e,$3f,$40,$a7,$0b,$3f,$b6,$19 + DATA BYTE $09,$3e,$10,$9f,$70,$60,$d0,$00 + DATA BYTE $60,$40,$7f,$3c,$63,$43,$7c,$04 + DATA BYTE $70,$ce,$8e,$70,$40,$da,$1c,$87 + DATA BYTE $dd,$ab,$98,$df,$70,$06,$cc,$33 + DATA BYTE $33,$68,$fc,$0f,$c5,$1c,$c5,$00 + DATA BYTE $28,$af,$1d,$8b,$c0,$db,$db,$ff + DATA BYTE $3d,$42,$17,$42,$3f,$1d,$ff,$c1 + DATA BYTE $a9,$04,$fa,$bd,$60,$e5,$e5,$1e + DATA BYTE $32,$cd,$3b,$cd,$3e,$03,$94,$1f + DATA BYTE $c0,$51,$cb,$38,$19,$80,$82,$da + DATA BYTE $37,$ba,$1f,$33,$f2,$0a,$ca,$ca + DATA BYTE $1f,$03,$9e,$5c,$07,$e7,$40,$60 + DATA BYTE $98,$98,$e0,$01,$82,$9f,$b2,$52 + DATA BYTE $38,$38,$03,$c0,$93,$43,$02,$92 + DATA BYTE $60,$03,$0f,$82,$7c,$7c,$30,$80 + DATA BYTE $20,$e3,$58,$04,$03,$0c,$cf,$03 + DATA BYTE $87,$f3,$e3,$df,$eb,$c3,$e2,$e3 + DATA BYTE $f8,$f7,$89,$04,$f3,$84,$8b,$10 + DATA BYTE $e3,$df,$ed,$5b,$6e,$13,$e3,$26 + DATA BYTE $3b,$3c,$03,$1b,$97,$76,$1f,$6f + DATA BYTE $3f,$3c,$9f,$c7,$39,$cc,$3d,$f7 + DATA BYTE $fb,$d8,$03,$13,$80,$ce,$c7,$67 + DATA BYTE $80,$5f,$f2,$cf,$71,$6f,$10,$0f + DATA BYTE $85,$b7,$c5,$f7,$d8,$f7,$4f,$99 + DATA BYTE $00,$0e,$97,$c5,$02,$cf,$00,$9d + DATA BYTE $3b,$9f,$dc,$43,$fd,$ec,$07,$9f + DATA BYTE $52,$f7,$01,$26,$8b,$b8,$0c,$cd + DATA BYTE $1d,$ff,$01,$e2,$ff,$0d,$b3,$b2 + DATA BYTE $af,$10,$18,$8b,$f0,$e0,$c7,$00 + DATA BYTE $07,$e3,$80,$e1,$61,$a1,$c0,$e3 + DATA BYTE $38,$23,$e1,$a9,$bb,$59,$af,$8d + DATA BYTE $04,$9a,$e0,$10,$c1,$d2,$54,$86 + DATA BYTE $82,$07,$fb,$5f,$15,$f3,$f7,$8c + DATA BYTE $03,$e7,$db,$d2,$07,$90,$6a,$26 + DATA BYTE $97,$78,$07,$ff,$0e,$b7,$8b,$d7 + DATA BYTE $c2,$cf,$c0,$c0,$33,$c0,$dc,$00 + DATA BYTE $dc,$63,$c7,$43,$9f,$3b,$03,$07 + DATA BYTE $a9,$4f,$07,$a1,$43,$2f,$ff,$d0 + DATA BYTE $3f,$88,$e3,$08,$14,$01,$14,$08 + DATA BYTE $0c,$f1,$f1,$ac,$1c,$1b,$d6,$fd + DATA BYTE $4a,$03,$d2,$d8,$fd,$a7,$57,$b0 + DATA BYTE $a0,$e0,$7b,$48,$f7,$b7,$f8,$06 + DATA BYTE $71,$ff,$8e,$ff,$60,$23,$3d,$20 + DATA BYTE $bd,$bf,$3d,$24,$ff,$00,$04,$b1 + DATA BYTE $00,$a7,$81,$e1,$e4,$1b,$1b,$e7 + DATA BYTE $f6,$17,$68,$0f,$83,$1c,$03,$30 + DATA BYTE $81,$f6,$3c,$84,$e7,$be,$4c,$f2 + DATA BYTE $bf,$fe,$01,$e2,$1d,$1d,$fe,$c2 + DATA BYTE $3d,$3d,$a2,$03,$82,$d2,$b2,$fe + DATA BYTE $83,$bf,$2a,$2f,$92,$28,$cb,$82 + DATA BYTE $d0,$d0,$10,$50,$96,$50,$50,$64 + DATA BYTE $9b,$9b,$11,$64,$7c,$83,$15,$3f + DATA BYTE $c0,$1f,$91,$d9,$03,$a3,$b0,$f0 + DATA BYTE $49,$8f,$50,$03,$3f,$03,$9c,$9c + DATA BYTE $00,$e1,$0f,$fc,$02,$02,$22,$05 + DATA BYTE $03,$5d,$f8,$07,$e5,$0f,$34,$e3 + DATA BYTE $3f,$20,$02,$b3,$b3,$33,$20,$bd + DATA BYTE $bd,$e3,$51,$a7,$a7,$27,$c0,$0f + DATA BYTE $00,$a3,$a3,$22,$00,$25,$a1,$a1 + DATA BYTE $eb,$5c,$f9,$00,$e7,$45,$00,$9f + DATA BYTE $00,$a8,$bb,$13,$df,$be,$f2,$ce + DATA BYTE $57,$03,$36,$af,$f7,$a3,$22,$84 + DATA BYTE $00,$00,$44,$00,$d1,$9f,$bb,$03 + DATA BYTE $96,$13,$f7,$00,$a2,$1f,$41,$00 + DATA BYTE $30,$00,$10,$85,$80,$f6,$f6,$e0 + DATA BYTE $68,$7e,$76,$29,$78,$08,$1a,$13 + DATA BYTE $af,$40,$00,$43,$bd,$bf,$02,$d7 + DATA BYTE $d5,$10,$d7,$00,$83,$00,$40,$0a + DATA BYTE $6a,$0b,$0a,$40,$02,$42,$aa,$50 + DATA BYTE $40,$00,$ca,$ff,$75,$b8,$d4,$ff + DATA BYTE $71,$c5,$f3,$5d,$30,$bb,$2d,$85 + DATA BYTE $f3,$e1,$eb,$e3,$fe,$0c,$10,$3d + DATA BYTE $0c,$43,$b6,$9d,$00,$f3,$ee,$28 + DATA BYTE $e7,$eb,$b8,$d7,$95,$e0,$a1,$b3 + DATA BYTE $e1,$bb,$95,$20,$5b,$00,$ca,$fb + DATA BYTE $e3,$c9,$03,$10,$08,$00,$82,$3c + DATA BYTE $3c,$f8,$f1,$db,$4c,$80,$3e,$2f + DATA BYTE $e7,$03,$9e,$04,$b8,$8b,$a1,$30 + DATA BYTE $78,$7c,$04,$74,$93,$9b,$d0,$0b + DATA BYTE $68,$68,$40,$c6,$3b,$70,$8f,$9b + DATA BYTE $6d,$49,$03,$54,$0a,$13,$17,$82 + DATA BYTE $b8,$e7,$41,$21,$27,$06,$27,$dd + DATA BYTE $32,$bb,$cc,$0c,$03,$76,$de,$03 + DATA BYTE $30,$0e,$1e,$07,$17,$88,$4b,$88 + DATA BYTE $e7,$1e,$63,$1f,$43,$12,$af,$03 + DATA BYTE $e3,$10,$9f,$13,$2c,$2c,$92,$94 + DATA BYTE $1e,$30,$0f,$03,$9e,$77,$89,$ef + DATA BYTE $bd,$36,$c3,$62,$08,$8b,$4e,$36 + DATA BYTE $fe,$fb,$14,$f1,$7b,$f1,$5e,$27 + DATA BYTE $f7,$ff,$0c,$7e,$81,$81,$ff,$8f + DATA BYTE $28,$9d,$c0,$20,$20,$df,$26,$d9 + DATA BYTE $d9,$07,$3e,$44,$bb,$bb,$7c,$51 + DATA BYTE $4b,$0d,$af,$f1,$e3,$77,$0b,$07 + DATA BYTE $07,$c6,$39,$39,$fe,$10,$df,$38 + DATA BYTE $c6,$c6,$2f,$38,$80,$ce,$22,$80 + DATA BYTE $40,$a3,$71,$c0,$e3,$02,$1b,$e4 + DATA BYTE $e4,$1f,$5f,$ff,$26,$5f,$53,$fb + DATA BYTE $0f,$90,$03,$07,$db,$57,$e6,$00 + DATA BYTE $bf,$1f,$0a,$f5,$3b,$f5,$0a,$11 + DATA BYTE $c6,$03,$f1,$3b,$a3,$e1,$b0,$03 + DATA BYTE $99,$dc,$c1,$03,$02,$04,$fd,$fd + DATA BYTE $03,$04,$c1,$e8,$e0,$0c,$91,$9d + DATA BYTE $9d,$7c,$44,$81,$0c,$2d,$90,$80 + DATA BYTE $64,$3c,$81,$03,$3e,$61,$81,$80 + DATA BYTE $a2,$80,$03,$1d,$80,$17,$d2,$bf + DATA BYTE $6b,$63,$00,$a7,$f3,$00,$03,$30 + DATA BYTE $e1,$f1,$e1,$18,$e1,$00,$fb,$e1 + DATA BYTE $09,$d0,$df,$d0,$04,$18,$48,$1e + DATA BYTE $fc,$e2,$92,$00,$02,$f3,$13,$e2 + DATA BYTE $83,$21,$c3,$09,$bb,$80,$c3,$e7 + DATA BYTE $c8,$03,$0c,$0f,$80,$04,$18,$31 + DATA BYTE $31,$11,$cf,$e2,$79,$00,$77,$00 + DATA BYTE $d2,$03,$c2,$30,$1f,$40,$8f,$8f + DATA BYTE $87,$9a,$06,$03,$02,$0d,$0f,$8c + DATA BYTE $03,$01,$5a,$63,$01,$2f,$18,$33 + DATA BYTE $2f,$67,$a3,$bf,$63,$b7,$f3,$03 + DATA BYTE $8b,$e3,$e2,$03,$40,$24,$00,$21 + DATA BYTE $00,$7c,$00,$00,$74,$74,$12,$ab + DATA BYTE $34,$00,$e6,$c3,$65,$e0,$b7,$23 + DATA BYTE $e2,$47,$0d,$60,$b4,$dd,$5f,$5f + DATA BYTE $09,$4f,$00,$17,$17,$db,$96,$47 + DATA BYTE $00,$31,$cf,$86,$08,$0a,$d3,$02 + DATA BYTE $eb,$73,$7e,$80,$b2,$03,$db,$ee + DATA BYTE $40,$03,$5e,$70,$3a,$a2,$00,$01 + DATA BYTE $01,$e2,$e4,$e2,$0a,$0e,$70,$00 + DATA BYTE $12,$2f,$70,$50,$2c,$11,$00,$0d + DATA BYTE $18,$bf,$a7,$bf,$2c,$fb,$fc,$ec + DATA BYTE $68,$fc,$97,$16,$51,$87,$cf,$dc + DATA BYTE $07,$e3,$48,$03,$07,$83,$01,$00 + DATA BYTE $c2,$c2,$c3,$01,$62,$62,$63,$01 + DATA BYTE $96,$db,$12,$89,$db,$06,$96,$cf + DATA BYTE $0e,$c0,$03,$02,$f9,$06,$06,$72 + DATA BYTE $f9,$eb,$f1,$f7,$53,$cc,$91,$dd + DATA BYTE $d5,$9d,$77,$61,$03,$13,$f7,$7f + DATA BYTE $47,$83,$a1,$0d,$ee,$82,$bb,$8b + DATA BYTE $a0,$c7,$c7,$e0,$13,$ad,$c3,$f3 + DATA BYTE $dd,$c3,$cf,$83,$d8,$07,$ff,$c0 + DATA BYTE $f0,$3c,$c3,$c3,$fc,$c8,$97,$64 + DATA BYTE $78,$27,$72,$7e,$83,$cb,$88,$cf + DATA BYTE $d8,$27,$3b,$27,$f8,$4b,$15,$bf + DATA BYTE $08,$dd,$17,$03,$84,$cf,$02,$0c + DATA BYTE $06,$0b,$09,$06,$f7,$22,$0e,$88 + DATA BYTE $ee,$10,$8c,$8c,$73,$73,$63,$8c + DATA BYTE $17,$03,$4a,$03,$b6,$b6,$03,$1a + DATA BYTE $fb,$fb,$0b,$46,$03,$6b,$07,$18 + DATA BYTE $70,$03,$cf,$31,$1e,$03,$17,$8f + DATA BYTE $03,$05,$08,$03,$24,$c8,$00,$04 + DATA BYTE $da,$89,$00,$04,$db,$00,$c0,$84 + DATA BYTE $e3,$80,$00,$39,$3d,$39,$84,$3a + DATA BYTE $3e,$3a,$08,$c1,$ef,$e6,$03,$10 + DATA BYTE $f5,$b3,$04,$71,$71,$01,$08,$03 + DATA BYTE $09,$53,$00,$af,$08,$8c,$ba,$ba + DATA BYTE $aa,$af,$5b,$00,$4a,$d3,$40,$2f + DATA BYTE $00,$02,$21,$23,$23,$02,$43,$01 + DATA BYTE $84,$61,$7c,$90,$20,$c1,$cc,$e1 + DATA BYTE $bb,$22,$38,$fc,$00,$1d,$fc,$b0 + DATA BYTE $0e,$0b,$de,$03,$7c,$40,$8b,$58 + DATA BYTE $87,$05,$f9,$7c,$23,$91,$bb,$42 + DATA BYTE $bc,$4e,$00,$b0,$ac,$82,$80,$2e + DATA BYTE $01,$90,$00,$9c,$2d,$08,$a0,$26 + DATA BYTE $26,$b1,$22,$83,$d8,$38,$0a,$72 + DATA BYTE $05,$07,$05,$06,$e0,$06,$00,$ce + DATA BYTE $16,$bf,$b2,$dc,$03,$63,$7f,$03 + DATA BYTE $3f,$7c,$e6,$27,$c0,$3f,$b7,$8c + DATA BYTE $27,$0f,$f2,$67,$07,$46,$c7,$31 + DATA BYTE $03,$5b,$03,$1b,$8f,$03,$1f,$3b + DATA BYTE $1b,$03,$03,$b2,$3e,$70,$96,$39 + DATA BYTE $00,$20,$f6,$44,$47,$9b,$71,$03 + DATA BYTE $c3,$73,$83,$61,$a3,$32,$3f,$f7 + DATA BYTE $8a,$1f,$0e,$87,$c9,$0f,$06,$a7 + DATA BYTE $80,$07,$03,$0c,$0c,$03,$41,$41 + DATA BYTE $02,$f5,$c5,$c3,$85,$97,$e9,$f7 + DATA BYTE $5f,$57,$fb,$76,$b7,$68,$63,$03 + DATA BYTE $67,$bb,$a3,$4b,$f3,$ef,$2f,$b2 + DATA BYTE $03,$c3,$d8,$ff,$c7,$4d,$88,$3f + DATA BYTE $8f,$ec,$29,$c3,$47,$01,$60,$03 + DATA BYTE $9a,$f7,$d3,$2b,$38,$77,$d6,$a5 + DATA BYTE $f7,$09,$03,$7d,$83,$82,$03,$03 + DATA BYTE $1b,$fb,$fd,$ce,$03,$97,$50,$e1 + DATA BYTE $1f,$1e,$1f,$e2,$c3,$60,$00,$03 + DATA BYTE $80,$db,$5b,$85,$03,$fb,$7b,$fb + DATA BYTE $bb,$b7,$77,$d9,$03,$4f,$ba,$3c + DATA BYTE $03,$01,$03,$6b,$1f,$8c,$1b,$40 + DATA BYTE $03,$ee,$cf,$78,$f0,$03,$1c,$ce + DATA BYTE $cb,$23,$3f,$fe,$07,$7e,$ff,$67 + DATA BYTE $03,$7f,$99,$03,$12,$46,$d3,$22 + DATA BYTE $00,$b8,$c3,$5d,$7c,$03,$07,$30 + DATA BYTE $03,$bc,$03,$3c,$7e,$98,$00,$3d + DATA BYTE $03,$3e,$c6,$00,$1e,$68,$03,$ca + DATA BYTE $32,$af,$d3,$ee,$15,$cb,$03,$11 + DATA BYTE $07,$04,$c2,$cb,$00,$07,$c1,$2f + DATA BYTE $2e,$2f,$c2,$23,$03,$21,$22,$c3 + DATA BYTE $23,$20,$23,$89,$8b,$07,$bd,$03 + DATA BYTE $46,$e7,$b4,$0b,$b2,$7c,$a7,$5b + DATA BYTE $a3,$03,$7f,$ef,$17,$f1,$03,$01 + DATA BYTE $e3,$07,$fe,$c6,$03,$fc,$26,$03 + DATA BYTE $ee,$fd,$cb,$a7,$8e,$bd,$e9,$58 + DATA BYTE $a6,$78,$e4,$b9,$e8,$bd,$67,$b3 + DATA BYTE $7a,$01,$31,$0e,$04,$01,$02,$96 + DATA BYTE $01,$15,$ca,$17,$60,$f3,$01,$7f + DATA BYTE $12,$ed,$ed,$1e,$1c,$d4,$8a,$5f + DATA BYTE $d5,$3a,$57,$01,$94,$e3,$e0,$c1 + DATA BYTE $d7,$d8,$df,$a4,$c2,$f6,$20,$aa + DATA BYTE $e3,$4d,$2c,$f0,$f1,$16,$f2,$7e + DATA BYTE $ef,$0c,$d3,$bd,$44,$ab,$52,$05 + DATA BYTE $67,$0b,$20,$83,$56,$61,$13,$e3 + DATA BYTE $01,$14,$03,$80,$5f,$ae,$78,$e2 + DATA BYTE $ac,$f1,$03,$66,$d0,$97,$31,$f0 + DATA BYTE $23,$e1,$81,$83,$c5,$e7,$12,$b3 + DATA BYTE $bf,$eb,$3f,$4d,$03,$89,$c7,$60 + DATA BYTE $e1,$e3,$74,$03,$89,$9b,$b1,$00 + DATA BYTE $fc,$fe,$8f,$00,$ff,$59,$00,$1e + DATA BYTE $84,$cb,$1e,$c7,$07,$3c,$9d,$03 + DATA BYTE $0b,$bc,$9f,$e0,$41,$33,$32,$23 + DATA BYTE $00,$4c,$30,$00,$01,$03,$48,$05 + DATA BYTE $02,$20,$b3,$2e,$00,$bf,$05,$06 + DATA BYTE $83,$fc,$03,$10,$63,$02,$e7,$31 + DATA BYTE $73,$0f,$d2,$8f,$03,$d0,$18,$03 + DATA BYTE $d3,$03,$d8,$c6,$03,$49,$6f,$2b + DATA BYTE $13,$cf,$1f,$ab,$97,$c6,$03,$bf + DATA BYTE $b8,$cb,$4d,$3f,$70,$f7,$99,$fc + DATA BYTE $05,$cc,$03,$e0,$03,$68,$60,$11 + DATA BYTE $be,$c6,$03,$d7,$31,$03,$d6,$03 + DATA BYTE $db,$c5,$85,$f9,$03,$f4,$81,$7d + DATA BYTE $0f,$ac,$00,$10,$f1,$07,$02,$e1 + DATA BYTE $83,$7c,$f9,$4c,$20,$01,$a6,$1b + DATA BYTE $01,$18,$2e,$01,$0c,$01,$ff,$2d + DATA BYTE $6f,$34,$9b,$e1,$a8,$03,$ee,$f4 + DATA BYTE $4f,$71,$20,$c7,$00,$62,$67,$02 + DATA BYTE $04,$c3,$c7,$03,$f4,$03,$21,$83 + DATA BYTE $87,$5e,$87,$8f,$07,$15,$e0,$ca + DATA BYTE $dd,$a7,$1d,$b1,$8c,$30,$9b,$91 + DATA BYTE $70,$80,$c0,$4d,$b7,$88,$d6,$af + DATA BYTE $e7,$87,$45,$e0,$46,$00,$9b,$f8 + DATA BYTE $11,$f0,$a1,$7f,$e0,$14,$01,$3f + DATA BYTE $82,$5d,$f0,$31,$00,$01,$03,$8b + DATA BYTE $8c,$03,$8f,$03,$79,$af,$03,$bc + DATA BYTE $ab,$56,$2a,$ac,$00,$fe,$f9,$ab + DATA BYTE $b1,$03,$f8,$e3,$03,$fc,$ef,$03 + DATA BYTE $f1,$9f,$f9,$03,$06,$94,$93,$02 + DATA BYTE $ce,$03,$07,$22,$00,$98,$00,$00 + DATA BYTE $f9,$00,$d2,$df,$30,$03,$90,$ff + DATA BYTE $b1,$ee,$4d,$84,$c3,$0f,$c2,$fb + DATA BYTE $e6,$03,$0c,$34,$03,$06,$b7,$0b + DATA BYTE $00,$8b,$07,$00,$f1,$00,$5e,$b7 + DATA BYTE $66,$0d,$31,$03,$29,$03,$55,$ae + DATA BYTE $13,$e3,$fe,$c3,$ab,$63,$f6,$03 + DATA BYTE $88,$3f,$03,$ff,$ff,$ff,$f0 + + ' Width = 16, height = 12 +image_pattern: + DATA BYTE $40,$00,$41,$00,$42,$00,$43,$00,$44,$00,$45,$00,$46,$00,$47,$00,$48,$00,$48,$00,$48,$00,$49,$00,$4a,$00,$4b,$00,$4c,$00,$4d,$00 + DATA BYTE $4e,$00,$4f,$00,$50,$00,$51,$00,$52,$00,$53,$00,$54,$00,$55,$00,$56,$00,$48,$00,$48,$00,$57,$00,$58,$00,$59,$00,$5a,$00,$5b,$00 + DATA BYTE $5c,$00,$5d,$00,$5e,$00,$5f,$00,$60,$00,$61,$00,$62,$00,$63,$00,$64,$00,$65,$00,$66,$00,$67,$00,$68,$00,$69,$00,$6a,$00,$6b,$00 + DATA BYTE $6c,$00,$6d,$00,$6e,$00,$6f,$00,$70,$00,$71,$00,$72,$00,$73,$00,$74,$00,$75,$00,$76,$00,$77,$00,$78,$00,$79,$00,$7a,$00,$7b,$00 + DATA BYTE $7c,$00,$7d,$00,$7e,$00,$7f,$00,$80,$00,$81,$00,$82,$00,$83,$00,$84,$00,$85,$00,$86,$00,$87,$00,$88,$00,$89,$00,$8a,$00,$8b,$00 + DATA BYTE $8c,$00,$8d,$00,$8e,$00,$8f,$00,$90,$00,$91,$00,$92,$00,$93,$00,$94,$00,$95,$00,$96,$00,$97,$00,$98,$00,$99,$00,$9a,$00,$9b,$00 + DATA BYTE $9c,$00,$9d,$00,$9e,$00,$9f,$00,$a0,$00,$a1,$00,$a2,$00,$a3,$00,$a4,$00,$a5,$00,$a6,$00,$a7,$00,$a8,$00,$a9,$00,$aa,$00,$ab,$00 + DATA BYTE $ac,$00,$ad,$00,$ae,$00,$af,$00,$b0,$00,$b1,$00,$b2,$00,$b3,$00,$b4,$00,$b5,$00,$b6,$00,$b7,$00,$b8,$00,$b9,$00,$ba,$00,$bb,$00 + DATA BYTE $bc,$00,$bd,$00,$be,$00,$bf,$00,$c0,$00,$c1,$00,$c2,$00,$c3,$00,$c4,$00,$c5,$00,$c6,$00,$c7,$00,$c8,$00,$c9,$00,$ca,$00,$cb,$00 + DATA BYTE $6d,$00,$cc,$00,$cd,$00,$ce,$00,$cf,$00,$d0,$00,$d1,$00,$d2,$00,$d3,$00,$d4,$00,$d5,$00,$d6,$00,$d7,$00,$d8,$00,$d9,$00,$da,$00 + DATA BYTE $db,$00,$dc,$00,$dd,$00,$de,$00,$df,$00,$e0,$00,$e1,$00,$e2,$00,$e3,$00,$48,$00,$e4,$00,$e5,$00,$e6,$00,$e7,$00,$e8,$00,$e9,$00 + DATA BYTE $ea,$00,$eb,$00,$ec,$00,$ed,$00,$ee,$00,$ef,$00,$f0,$00,$f1,$00,$f2,$00,$48,$00,$f3,$00,$f4,$00,$f5,$00,$f6,$00,$f7,$00,$f8,$00 diff --git a/examples/brinquitos_nes.bas b/examples/brinquitos_nes.bas new file mode 100644 index 0000000..6d009c3 --- /dev/null +++ b/examples/brinquitos_nes.bas @@ -0,0 +1,377 @@ + ' + ' Brinquitos: Demo game + ' + ' by Oscar Toledo Gutiérrez + ' https://nanochess.org/ + ' + ' Creation date: Oct/06/2024. + ' Revision date: Aug/24/2025. Adapted to NES/Famicom. + ' + + CONST MAX_CLOUDS = 4 + + ' + ' Cloud information + ' + DIM cloud_x(MAX_CLOUDS) ' X-coordinate + DIM cloud_y(MAX_CLOUDS) ' Y-coordinate + DIM cloud_e(MAX_CLOUDS) ' State + + PALETTE LOAD game_palette + + ' + ' Restart game + ' +restart_game: + ' Clear the screen + CLS + + ' Green letters in the center + VPOKE $23C8,$55 + VPOKE $23C9,$55 + VPOKE $23CA,$55 + VPOKE $23CB,$55 + VPOKE $23CC,$55 + VPOKE $23CD,$55 + VPOKE $23CE,$55 + VPOKE $23CF,$55 + + #puntos = 0 + + ' Setup initial clouds + cloud_y(3) = 176 + cloud_y(2) = 126 + cloud_y(1) = 76 + cloud_y(0) = 26 + + cloud_x(3) = 128 + cloud_e(3) = 0 + + c = 2 + GOSUB setup_cloud + c = 1 + GOSUB setup_cloud + c = 0 + GOSUB setup_cloud + + ' Setup player + x = 128 ' X-coordinate + y = 160 ' Y-coordinate + jump_state = 0 ' Jump state + standing_on = 3 ' Standing on cloud 3 + + PLAY SIMPLE + PLAY music_gladiators + + ' + ' Main loop + ' +main_loop: + + ' + ' Jump sound effect. + ' + IF jump_state >= 33 THEN ' Jumping + f = 4 + SOUND 12, $0800 + y, 64 + SOUND 15, 15 + ELSEIF jump_state THEN ' Falling + f = 8 + SOUND 12, $0800 + y, 64 + SOUND 15, 15 + ELSE ' Standing + f = 0 + SOUND 15, 11 + END IF + + ' Show the player sprite + SPRITE 0, y - 1, x - 8, f + 1, 0 + SPRITE 1, y - 1, x, f + 3, 0 + + ' Show sprites for clouds + FOR c = 0 TO 3 + SPRITE c * 2 + 2, cloud_y(c) - 1, cloud_x(c) - 8, 12 + 1, 1 + SPRITE c * 2 + 3, cloud_y(c) - 1, cloud_x(c), 14 + 1, 1 + NEXT c + + ' Video synchronization + WAIT + + ' Update score + PRINT AT 65,<>#puntos + + ' Detect if it should do vertical displacement... + ' ...of the screen. + IF y < 110 AND jump_state = 0 THEN + y = y + 1 + FOR c = 0 TO MAX_CLOUDS - 1 + cloud_y(c) = cloud_y(c) + 1 + IF cloud_y(c) = 192 THEN + cloud_y(c) = -8 + GOSUB setup_cloud + END IF + NEXT c + END IF + + ' Detect if the player stands on a cloud + IF jump_state <> 0 AND jump_state < 33 AND standing_on = 255 THEN + FOR c = 0 TO MAX_CLOUDS - 1 + IF ABS(cloud_x(c) + 1 - x) < 8 AND ABS(cloud_y(c) - 16 - y) < 3 THEN + jump_state = 0 + y = cloud_y(c) - 16 + standing_on = c + IF was_on <> standing_on THEN + #puntos = #puntos + 1 + END IF + END IF + NEXT c + END IF + + ' Move the clouds + FOR c = 0 TO MAX_CLOUDS - 1 + IF cloud_e(c) = 1 THEN ' 1- Left + IF standing_on = c THEN x = x - 2 + cloud_x(c) = cloud_x(c) - 2 + IF cloud_x(c) < 18 THEN cloud_e(c) = 2 + ELSEIF cloud_e(c) = 2 THEN ' 2- Right + IF standing_on = c THEN x = x + 2 + cloud_x(c) = cloud_x(c) + 2 + IF cloud_x(c) > 238 THEN cloud_e(c) = 1 + END IF + NEXT c + + ' Handle jumping. + IF jump_state THEN + IF jump_state >= 33 THEN + jump_state = jump_state - 1 + y = y - 2 + ELSE + y = y + 2 + IF y = 242 OR y = 243 THEN GOTO end_of_game + END IF + END IF + + ' Jump if button pressed + IF cont.button THEN + IF jump_state = 0 THEN + jump_state = 64 + was_on = standing_on + standing_on = 255 + END IF + END IF + + GOTO main_loop + + ' + ' End of game + ' +end_of_game: + ' Turn off jumping sound effect + SOUND 15, 11 + + ' Final music + PLAY music_end + + PRINT AT 168,"G A M E O V E R" + + FOR c = 1 TO 240 + WAIT + NEXT c + + GOTO restart_game + + ' + ' Setup a cloud + ' +setup_cloud: PROCEDURE + + ' + ' Choose a position with margin of 32 pixels at each side. + ' Make sure the minimum distance of 64 pixels... + ' ...against the next cloud so the jump is possible. + ' + DO + cloud_x(c) = RANDOM(96) * 2 + 32 + LOOP WHILE ABS(cloud_x(c) + 1 - cloud_x((c + 1) % MAX_CLOUDS)) < 64 + + ' Assign a movement direction. + cloud_e(c) = RANDOM(2) + 1 + END + +game_palette: + DATA BYTE $02,$27,$27,$27 + DATA BYTE $02,$27,$27,$2A + DATA BYTE $02,$27,$27,$27 + DATA BYTE $02,$27,$27,$27 + DATA BYTE $02,$27,$16,$12 + DATA BYTE $02,$0F,$10,$30 + DATA BYTE $02,$27,$27,$27 + DATA BYTE $02,$27,$27,$27 + + ' + ' Game sprites + ' + CHRROM 0 + + CHRROM PATTERN 256 + + BITMAP "................" ' Standing + BITMAP "................" + BITMAP "................" + BITMAP "......1111......" + BITMAP ".....111111....." + BITMAP "....11111111...." + BITMAP "...1133113311..." + BITMAP "...1133113311..." + BITMAP "..111111111111.." + BITMAP ".11111311311111." + BITMAP "11.1..1111..1.11" + BITMAP "11.11......11.11" + BITMAP "11.1111111111.11" + BITMAP "222..11..11..222" + BITMAP "....222..222...." + BITMAP "...2222..2222..." + + BITMAP ".22..........22." ' Jumping + BITMAP "222..........222" + BITMAP "111...1111...111" + BITMAP ".11..111111..11." + BITMAP ".11.13311331.11." + BITMAP ".1.1133113311.1." + BITMAP ".1.1111111111.1." + BITMAP "..111131131111.." + BITMAP "...1..1111..1..." + BITMAP "...11..11..11..." + BITMAP "...111....111..." + BITMAP "...1111111111..." + BITMAP ".....11..11....." + BITMAP ".....11..11....." + BITMAP ".....22..22....." + BITMAP "....222..222...." + + BITMAP ".22..........22." ' Falling + BITMAP "222...1111...222" + BITMAP ".11..111111..11." + BITMAP ".11.13311331.11." + BITMAP ".1.1133113311.1." + BITMAP ".1.1111111111.1." + BITMAP "..111131131111.." + BITMAP "...1111331111..." + BITMAP "...11......11..." + BITMAP "...111....111..." + BITMAP "...1111111111..." + BITMAP ".....11..11....." + BITMAP ".....11..11....." + BITMAP ".....22..22....." + BITMAP ".....22..22....." + BITMAP "....222..222...." + + BITMAP "..333133313....." ' Cloud + BITMAP ".333321332132..." + BITMAP ".3322331332132.." + BITMAP "33211331332132.." + BITMAP "22133311223112.." + BITMAP ".133313311132..." + BITMAP ".3333133213332.." + BITMAP ".33331332133332." + BITMAP "..2222..22..22.." + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP "................" + + ' + ' Gladiators music (circus entry) + ' +music_gladiators: + DATA BYTE 7 + MUSIC C5,C3,-,- + MUSIC S,S,-,- + MUSIC B4,G3,-,- + MUSIC S,S,-,- + MUSIC A4#,G2,-,- + MUSIC B4,S,-,- + MUSIC A4#,G3,-,- + MUSIC A4,S,-,- + + MUSIC G4#,C3,-,- + MUSIC S,S,-,- + MUSIC G4,G3,-,- + MUSIC S,S,-,- + MUSIC F4#,G2,-,- + MUSIC S,S,-,- + MUSIC G4,G3,-,- + MUSIC S,S,-,- + + MUSIC A4,C3,-,- + MUSIC S,S,-,- + MUSIC G4#,G3,-,- + MUSIC S,S,-,- + MUSIC G4,G2,-,- + MUSIC G4#,S,-,- + MUSIC G4,G3,-,- + MUSIC F4#,S,-,- + + MUSIC F4,C3,-,- + MUSIC S,S,-,- + MUSIC E4,G3,-,- + MUSIC S,S,-,- + MUSIC D4#,G2,-,- + MUSIC S,S,-,- + MUSIC E4,G3,-,- + MUSIC S,S,-,- + + MUSIC G4,B2,-,- + MUSIC S,S,-,- + MUSIC F4,G3,-,- + MUSIC F4,S,-,- + MUSIC C4#,G2,-,- + MUSIC S,S,-,- + MUSIC D4,G3,-,- + MUSIC S,S,-,- + + MUSIC G4,B2,-,- + MUSIC S,S,-,- + MUSIC F4,G3,-,- + MUSIC F4,S,-,- + MUSIC C4#,G2,-,- + MUSIC S,S,-,- + MUSIC D4,G3,-,- + MUSIC S,S,-,- + + MUSIC B3,B2,-,- + MUSIC C4,S,-,- + MUSIC C4S,G3,-,- + MUSIC D4,S,-,- + MUSIC D4S,G2,-,- + MUSIC E4,S,-,- + MUSIC F4,G3,-,- + MUSIC F4S,S,-,- + + MUSIC G4,B2,-,- + MUSIC G4S,S,-,- + MUSIC A4,G3,-,- + MUSIC B4,S,-,- + MUSIC A4,G2,-,- + MUSIC S,S,-,- + MUSIC G4,G3,-,- + MUSIC S,S,-,- + + MUSIC REPEAT + + ' + ' Music for end of game. + ' +music_end: + DATA BYTE 20 + MUSIC C4,-,-,- + MUSIC G3,-,-,- + MUSIC C3,-,-,- + MUSIC S,-,-,- + MUSIC S,-,-,- + MUSIC S,-,-,- + MUSIC STOP + diff --git a/examples/brinquitos_sms.bas b/examples/brinquitos_sms.bas new file mode 100644 index 0000000..fe05475 --- /dev/null +++ b/examples/brinquitos_sms.bas @@ -0,0 +1,361 @@ + ' + ' Brinquitos: Demo game + ' + ' by Oscar Toledo Gutiérrez + ' https://nanochess.org/ + ' + ' Creation date: Oct/06/2024. + ' Revision date: Apr/26/2025. Adapted to Sega Master System. + ' + + CONST MAX_CLOUDS = 4 + + ' + ' Cloud information + ' + DIM cloud_x(MAX_CLOUDS) ' X-coordinate + DIM cloud_y(MAX_CLOUDS) ' Y-coordinate + DIM cloud_e(MAX_CLOUDS) ' State + + ' + ' Restart game + ' +restart_game: + ' Clear the screen + CLS + ' Turquoise numbers + FOR #c = $0603 TO $07FF STEP 4 + VPOKE #c, 0 + NEXT #c + ' Blue background + FOR #c = $0402 TO $0FFE STEP 4 + VPOKE #c, VPEEK(#c) OR $FF + NEXT #c + + ' Define sprites + DEFINE SPRITE 0,8,sprites_bitmaps + + #puntos = 0 + + ' Setup initial clouds + cloud_y(3) = 176 + cloud_y(2) = 126 + cloud_y(1) = 76 + cloud_y(0) = 26 + + cloud_x(3) = 128 + cloud_e(3) = 0 + + c = 2 + GOSUB setup_cloud + c = 1 + GOSUB setup_cloud + c = 0 + GOSUB setup_cloud + + ' Setup player + x = 128 ' X-coordinate + y = 160 ' Y-coordinate + jump_state = 0 ' Jump state + standing_on = 3 ' Standing on cloud 3 + + PLAY SIMPLE + PLAY music_gladiators + + ' + ' Main loop + ' +main_loop: + + ' + ' Jump sound effect. + ' + IF jump_state >= 33 THEN ' Jumping + f = 4 + SOUND 2, y, 12 + ELSEIF jump_state THEN ' Falling + f = 8 + SOUND 2, y, 12 + ELSE ' Standing + f = 0 + SOUND 2, , 0 + END IF + + ' Show the player sprite + SPRITE 0, y - 1, x - 8, f + SPRITE 1, y - 1, x, f + 2 + + ' Show sprites for clouds + FOR c = 0 TO 3 + SPRITE c * 2 + 2, cloud_y(c) - 1, cloud_x(c) - 8, 12 + SPRITE c * 2 + 3, cloud_y(c) - 1, cloud_x(c), 14 + NEXT c + + ' Video synchronization + WAIT + + ' Update score + PRINT AT 1,<>#puntos + + ' Detect if it should do vertical displacement... + ' ...of the screen. + IF y < 110 AND jump_state = 0 THEN + y = y + 1 + FOR c = 0 TO MAX_CLOUDS - 1 + cloud_y(c) = cloud_y(c) + 1 + IF cloud_y(c) = 192 THEN + cloud_y(c) = -8 + GOSUB setup_cloud + END IF + NEXT c + END IF + + ' Detect if the player stands on a cloud + IF jump_state <> 0 AND jump_state < 33 AND standing_on = 255 THEN + FOR c = 0 TO MAX_CLOUDS - 1 + IF ABS(cloud_x(c) + 1 - x) < 8 AND ABS(cloud_y(c) - 16 - y) < 3 THEN + jump_state = 0 + y = cloud_y(c) - 16 + standing_on = c + IF was_on <> standing_on THEN + #puntos = #puntos + 1 + END IF + END IF + NEXT c + END IF + + ' Move the clouds + FOR c = 0 TO MAX_CLOUDS - 1 + IF cloud_e(c) = 1 THEN ' 1- Left + IF standing_on = c THEN x = x - 2 + cloud_x(c) = cloud_x(c) - 2 + IF cloud_x(c) < 18 THEN cloud_e(c) = 2 + ELSEIF cloud_e(c) = 2 THEN ' 2- Right + IF standing_on = c THEN x = x + 2 + cloud_x(c) = cloud_x(c) + 2 + IF cloud_x(c) > 238 THEN cloud_e(c) = 1 + END IF + NEXT c + + ' Handle jumping. + IF jump_state THEN + IF jump_state >= 33 THEN + jump_state = jump_state - 1 + y = y - 2 + ELSE + y = y + 2 + IF y = 200 OR y = 201 THEN GOTO end_of_game + END IF + END IF + + ' Jump if button pressed + IF cont.button THEN + IF jump_state = 0 THEN + jump_state = 64 + was_on = standing_on + standing_on = 255 + END IF + END IF + + GOTO main_loop + + ' + ' End of game + ' +end_of_game: + ' Turn off jumping sound effect + SOUND 2, , 0 + + ' Final music + PLAY music_end + + PRINT AT 135,"G A M E O V E R" + + FOR c = 1 TO 240 + WAIT + NEXT c + + GOTO restart_game + + ' + ' Setup a cloud + ' +setup_cloud: PROCEDURE + + ' + ' Choose a position with margin of 32 pixels at each side. + ' Make sure the minimum distance of 64 pixels... + ' ...against the next cloud so the jump is possible. + ' + DO + cloud_x(c) = RANDOM(96) * 2 + 32 + LOOP WHILE ABS(cloud_x(c) + 1 - cloud_x((c + 1) % MAX_CLOUDS)) < 64 + + ' Assign a movement direction. + cloud_e(c) = RANDOM(2) + 1 + END + + ' + ' Game sprites + ' +sprites_bitmaps: + BITMAP "................" ' Standing + BITMAP "................" + BITMAP "................" + BITMAP "......AAAA......" + BITMAP ".....AAAAAA....." + BITMAP "....AAAAAAAAA..." + BITMAP "...AA44AA44AA..." + BITMAP "...AA44AA44AA..." + BITMAP "..AAAAAAAAAAAA.." + BITMAP ".AAAAAFAAFAAAAA." + BITMAP "AA.A66AAAA66A.AA" + BITMAP "AA.AA666666AA.AA" + BITMAP "AA.AAAAAAAAAA.AA" + BITMAP "999..AA..AA..999" + BITMAP "....555..555...." + BITMAP "...5555..5555..." + + BITMAP ".99..........99." ' Jumping + BITMAP "999..........999" + BITMAP "AAA...AAAA...AAA" + BITMAP ".AA..AAAAAA..AA." + BITMAP ".AA.A44AA44A.AA." + BITMAP ".A.AA44AA44AA.A." + BITMAP ".A.AAAAAAAAAA.A." + BITMAP "..AAAAFAAFAAAA.." + BITMAP "...A66AAAA66A..." + BITMAP "...AA66AA66AA..." + BITMAP "...AAA6666AAA..." + BITMAP "...AAAAAAAAAA..." + BITMAP ".....AA..AA....." + BITMAP ".....AA..AA....." + BITMAP ".....55..55....." + BITMAP "....555..555...." + + BITMAP ".99..........99." ' Falling + BITMAP "999...AAAA...999" + BITMAP ".AA..AAAAAA..AA." + BITMAP ".AA.A44AA44A.AA." + BITMAP ".A.AA44AA44AA.A." + BITMAP ".A.AAAAAAAAAA.A." + BITMAP "..AAAAFAAFAAAA.." + BITMAP "...AAAAFFAAAA..." + BITMAP "...AA666666AA..." + BITMAP "...AAA6666AAA..." + BITMAP "...AAAAAAAAAA..." + BITMAP ".....AA..AA....." + BITMAP ".....AA..AA....." + BITMAP ".....55..55....." + BITMAP ".....55..55....." + BITMAP "....555..555...." + + BITMAP "..FFF1FFF1F....." ' Cloud + BITMAP ".FFFFE1FFE1FE..." + BITMAP ".FFEEFF1FFE1FE.." + BITMAP "FFE11FF1FFE1FE.." + BITMAP "EE1FFF11EEF11E.." + BITMAP ".1FFF1FF111FE..." + BITMAP ".FFFF1FFE1FFFE.." + BITMAP ".FFFF1FFE1FFFFE." + BITMAP "..EEEE..EE..EE.." + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP "................" + + ' + ' Gladiators music (circus entry) + ' +music_gladiators: + DATA BYTE 7 + MUSIC C5,C3,-,- + MUSIC S,S,-,- + MUSIC B4,G3,-,- + MUSIC S,S,-,- + MUSIC A4#,G2,-,- + MUSIC B4,S,-,- + MUSIC A4#,G3,-,- + MUSIC A4,S,-,- + + MUSIC G4#,C3,-,- + MUSIC S,S,-,- + MUSIC G4,G3,-,- + MUSIC S,S,-,- + MUSIC F4#,G2,-,- + MUSIC S,S,-,- + MUSIC G4,G3,-,- + MUSIC S,S,-,- + + MUSIC A4,C3,-,- + MUSIC S,S,-,- + MUSIC G4#,G3,-,- + MUSIC S,S,-,- + MUSIC G4,G2,-,- + MUSIC G4#,S,-,- + MUSIC G4,G3,-,- + MUSIC F4#,S,-,- + + MUSIC F4,C3,-,- + MUSIC S,S,-,- + MUSIC E4,G3,-,- + MUSIC S,S,-,- + MUSIC D4#,G2,-,- + MUSIC S,S,-,- + MUSIC E4,G3,-,- + MUSIC S,S,-,- + + MUSIC G4,B2,-,- + MUSIC S,S,-,- + MUSIC F4,G3,-,- + MUSIC F4,S,-,- + MUSIC C4#,G2,-,- + MUSIC S,S,-,- + MUSIC D4,G3,-,- + MUSIC S,S,-,- + + MUSIC G4,B2,-,- + MUSIC S,S,-,- + MUSIC F4,G3,-,- + MUSIC F4,S,-,- + MUSIC C4#,G2,-,- + MUSIC S,S,-,- + MUSIC D4,G3,-,- + MUSIC S,S,-,- + + MUSIC B3,B2,-,- + MUSIC C4,S,-,- + MUSIC C4S,G3,-,- + MUSIC D4,S,-,- + MUSIC D4S,G2,-,- + MUSIC E4,S,-,- + MUSIC F4,G3,-,- + MUSIC F4S,S,-,- + + MUSIC G4,B2,-,- + MUSIC G4S,S,-,- + MUSIC A4,G3,-,- + MUSIC B4,S,-,- + MUSIC A4,G2,-,- + MUSIC S,S,-,- + MUSIC G4,G3,-,- + MUSIC S,S,-,- + + MUSIC REPEAT + + ' + ' Music for end of game. + ' +music_end: + DATA BYTE 20 + MUSIC C4,-,-,- + MUSIC G3,-,-,- + MUSIC C3,-,-,- + MUSIC S,-,-,- + MUSIC S,-,-,- + MUSIC S,-,-,- + MUSIC STOP + diff --git a/examples/cats_sms.bas b/examples/cats_sms.bas new file mode 100644 index 0000000..4f08a4e --- /dev/null +++ b/examples/cats_sms.bas @@ -0,0 +1,348 @@ + ' + ' Animation of cats + ' by Oscar Toledo G. + ' Example for CVBasic + ' + ' Creation date: Apr/27/2025. For Sega Master System. + ' + + DIM x(8),y(8),f(8) + + FOR c = 0 TO 7 + x(c) = RANDOM(176) + 32 + y(c) = RANDOM(112) + 32 + IF c AND 1 THEN f(c) = 4 ELSE f(c) = 0 + NEXT c + + DEFINE SPRITE 0,16,cat_sprites + +main_loop: + WAIT + FOR c = 0 TO 7 + SPRITE c * 2 + 0, y(c), x(c), f(c) * 4 + SPRITE c * 2 + 1, y(c), x(c) + 8, f(c) * 4 + 2 + NEXT c + + IF (FRAME AND 7) = 0 THEN + FOR c = 0 TO 7 + f(c) = f(c) + 1 + IF f(c) = 4 THEN f(c) = 0 + IF f(c) = 8 THEN f(c) = 4 + NEXT c + END IF + + GOTO main_loop + + ' TMSColor 3.0 Apr/27/2025 + ' Command: ../tmscolor/tmscolor -sms -sb -b -i cats_sprites.bmp cats_sprites_sms.bas cat_sprites + ' Created: Sun Apr 27 13:34:22 2025 + + ' + ' Sample code: + ' DEFINE SPRITE 0,16,cat_sprites + ' +cat_sprites: + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "EE0000EE" + BITMAP "EE00000E" + BITMAP "EE00000F" + + BITMAP "EE00000E" + BITMAP "0EE00000" + BITMAP "0EE0EEEE" + BITMAP "00EEEEEE" + BITMAP "00EEEEEE" + BITMAP "0EEEEEEE" + BITMAP "0E555EE0" + BITMAP "EEEEE0E0" + + + BITMAP "00000000" + BITMAP "E000E000" + BITMAP "E500E000" + BITMAP "EE5EE000" + BITMAP "EE555000" + BITMAP "52252EE0" + BITMAP "52252E00" + BITMAP "FFE5EFFF" + + BITMAP "5EEEEE00" + BITMAP "FFEEFFF0" + BITMAP "E5EEE000" + BITMAP "EEE0E500" + BITMAP "EEE0E500" + BITMAP "05E0E500" + BITMAP "05E0E500" + BITMAP "0EE0EE00" + + + BITMAP "00000000" + BITMAP "0000000E" + BITMAP "0000000E" + BITMAP "0000000E" + BITMAP "0000000E" + BITMAP "EE000EE5" + BITMAP "EE0000E5" + BITMAP "EE0000FF" + + BITMAP "EE0000E5" + BITMAP "0EE0000F" + BITMAP "0EE0EEEE" + BITMAP "00EEEEEE" + BITMAP "00EEEEEE" + BITMAP "0EEEEEEE" + BITMAP "0E555EE0" + BITMAP "EEEEE0E0" + + + BITMAP "00000000" + BITMAP "000E0000" + BITMAP "500E0000" + BITMAP "E5EE0000" + BITMAP "E5550000" + BITMAP "2522EE00" + BITMAP "2522E000" + BITMAP "FE5EFFF0" + + BITMAP "EEEEE000" + BITMAP "FEEFFF00" + BITMAP "5EEE0000" + BITMAP "EEE0E500" + BITMAP "EEE0E500" + BITMAP "05E0E500" + BITMAP "05E0E500" + BITMAP "0EE0EE00" + + + BITMAP "0000000E" + BITMAP "0000000E" + BITMAP "0000000E" + BITMAP "0000000E" + BITMAP "00000EE5" + BITMAP "EE0000E5" + BITMAP "EE0000FF" + BITMAP "EE0000E5" + + BITMAP "EE00000F" + BITMAP "0EE00EEE" + BITMAP "0EE0EEEE" + BITMAP "00EEEEEE" + BITMAP "00EEEEEE" + BITMAP "0EEEEEEE" + BITMAP "0E555EE0" + BITMAP "EEEEE0E0" + + + BITMAP "00000000" + BITMAP "5000E000" + BITMAP "E5EEE000" + BITMAP "E5EE0000" + BITMAP "2555EE00" + BITMAP "2522E000" + BITMAP "FE22FFF0" + BITMAP "EEEEE000" + + BITMAP "FEEFFF00" + BITMAP "5EEE0000" + BITMAP "5EEE0000" + BITMAP "EEE0E500" + BITMAP "EEE0E500" + BITMAP "05E0E500" + BITMAP "05E0E500" + BITMAP "0EE0EE00" + + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "0000000E" + BITMAP "0000000E" + BITMAP "0000000E" + BITMAP "EE000EE5" + BITMAP "EE0000E5" + BITMAP "EE0000E5" + + BITMAP "EE0000FF" + BITMAP "0EE0000E" + BITMAP "0EE0EEEF" + BITMAP "00EEEEEE" + BITMAP "00EEEEEE" + BITMAP "0EEEEEEE" + BITMAP "0E555EE0" + BITMAP "EEEEE0E0" + + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "000E0000" + BITMAP "55EE0000" + BITMAP "E5550000" + BITMAP "5555EE00" + BITMAP "2522E000" + BITMAP "2E22E000" + + BITMAP "EEEEFFF0" + BITMAP "EEEEE000" + BITMAP "FEEFFF00" + BITMAP "EEE0E500" + BITMAP "EEE0E500" + BITMAP "05E0E500" + BITMAP "05E0E500" + BITMAP "0EE0EE00" + + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00008000" + BITMAP "00008855" + BITMAP "00005558" + BITMAP "00885555" + BITMAP "00082252" + BITMAP "00082282" + + BITMAP "0FFF8888" + BITMAP "00088888" + BITMAP "00FFF88F" + BITMAP "00580888" + BITMAP "00580888" + BITMAP "00580850" + BITMAP "00580850" + BITMAP "00880880" + + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "80000000" + BITMAP "80000000" + BITMAP "80000000" + BITMAP "58800088" + BITMAP "58000088" + BITMAP "58000088" + + BITMAP "FF000088" + BITMAP "80000880" + BITMAP "F8880880" + BITMAP "88888800" + BITMAP "88888800" + BITMAP "88888880" + BITMAP "08855580" + BITMAP "08088888" + + + BITMAP "00000000" + BITMAP "00080005" + BITMAP "00088858" + BITMAP "00008858" + BITMAP "00885552" + BITMAP "00082252" + BITMAP "0FFF228F" + BITMAP "00088888" + + BITMAP "00FFF88F" + BITMAP "00008885" + BITMAP "00008885" + BITMAP "00580888" + BITMAP "00580888" + BITMAP "00580850" + BITMAP "00580850" + BITMAP "00880880" + + + BITMAP "80000000" + BITMAP "80000000" + BITMAP "80000000" + BITMAP "80000000" + BITMAP "58800000" + BITMAP "58000088" + BITMAP "FF000088" + BITMAP "58000088" + + BITMAP "F0000088" + BITMAP "88800880" + BITMAP "88880880" + BITMAP "88888800" + BITMAP "88888800" + BITMAP "88888880" + BITMAP "08855580" + BITMAP "08088888" + + + BITMAP "00000000" + BITMAP "00008000" + BITMAP "00008005" + BITMAP "00008858" + BITMAP "00005558" + BITMAP "00882252" + BITMAP "00082252" + BITMAP "0FFF858F" + + BITMAP "00088888" + BITMAP "00FFF88F" + BITMAP "00008885" + BITMAP "00580888" + BITMAP "00580888" + BITMAP "00580850" + BITMAP "00580850" + BITMAP "00880880" + + + BITMAP "00000000" + BITMAP "80000000" + BITMAP "80000000" + BITMAP "80000000" + BITMAP "80000000" + BITMAP "58800088" + BITMAP "58000088" + BITMAP "FF000088" + + BITMAP "58000088" + BITMAP "F0000880" + BITMAP "88880880" + BITMAP "88888800" + BITMAP "88888800" + BITMAP "88888880" + BITMAP "08855580" + BITMAP "08088888" + + + BITMAP "00000000" + BITMAP "00080008" + BITMAP "00080058" + BITMAP "00088588" + BITMAP "00055588" + BITMAP "08825225" + BITMAP "00825225" + BITMAP "FFF858FF" + + BITMAP "00888885" + BITMAP "0FFF88FF" + BITMAP "00088858" + BITMAP "00580888" + BITMAP "00580888" + BITMAP "00580850" + BITMAP "00580850" + BITMAP "00880880" + + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "88000088" + BITMAP "80000088" + BITMAP "F0000088" + + BITMAP "80000088" + BITMAP "00000880" + BITMAP "88880880" + BITMAP "88888800" + BITMAP "88888800" + BITMAP "88888880" + BITMAP "08855580" + BITMAP "08088888" + + diff --git a/examples/controller.bas b/examples/controller.bas index 88771c5..c78db56 100644 --- a/examples/controller.bas +++ b/examples/controller.bas @@ -2,9 +2,12 @@ ' Controller test ' ' by Oscar Toledo G. + ' https://nanochess.org/ ' ' Creation date: Aug/21/2024. ' Revision date: Oct/15/2024. Solved wrong display for number key in 2nd controller. + ' Revision date: Nov/28/2025. Added support for Blue and Purple buttons (Super + ' Action Controller) ' CONST left_x = 56 @@ -37,7 +40,7 @@ d = VPEEK(#c) VPOKE #c, (d / 2) OR (d / 4) NEXT #c - DEFINE CHAR 42,2,number_bitmap + DEFINE CHAR 42,4,number_bitmap VDP(1) = $E0 ' 8x8 sprites VDP(6) = $00 ' Sprites use character bitmaps. @@ -162,6 +165,14 @@ prepare_key: PROCEDURE x = ((c - 1) % 3) * 16 y = 63 + ((c - 1) / 3) * 16 c = c + 48 + ELSEIF c = 12 THEN + x = 8 + y = 0 + c = 44 + ELSEIF c = 13 THEN + x = 8 + y = 8 + c = 45 ELSE ' Some systems have extra characters x = 16 @@ -251,3 +262,22 @@ number_bitmap: BITMAP "..XX.XX." BITMAP "..XX.XX." BITMAP "........" + + BITMAP "XXXXXXX." + BITMAP "XX....XX" + BITMAP "XX....XX" + BITMAP "XXXXXXX." + BITMAP "XX....XX" + BITMAP "XX....XX" + BITMAP "XXXXXXX." + BITMAP "........" + + BITMAP "XXXXXXX." + BITMAP "XX....XX" + BITMAP "XX....XX" + BITMAP "XXXXXXX." + BITMAP "XX......" + BITMAP "XX......" + BITMAP "XX......" + BITMAP "........" + diff --git a/examples/controller_nes.bas b/examples/controller_nes.bas new file mode 100644 index 0000000..5b804b8 --- /dev/null +++ b/examples/controller_nes.bas @@ -0,0 +1,82 @@ + ' + ' Controller bits (demo for CVBasic) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Aug/23/2025. + ' Revision date: Aug/23/2025. Adapted for NES. + ' + +game_loop: + WAIT + c = CONT1 + + SPRITE 16, 64, 96, (c AND $80) / 128 * 2, 0 + SPRITE 17, 64, 104, (c AND $40) / 64 * 2, 0 + SPRITE 18, 64, 112, (c AND $20) / 32 * 2, 0 + SPRITE 19, 64, 120, (c AND $10) / 16 * 2, 0 + SPRITE 20, 64, 128, (c AND $08) / 8 * 2, 0 + SPRITE 21, 64, 136, (c AND $04) / 4 * 2, 0 + SPRITE 22, 64, 144, (c AND $02) / 2 * 2, 0 + SPRITE 23, 64, 152, (c AND $01) / 1 * 2, 0 + + c = CONT1.KEY + + PRINT AT 335,<>c," " + + c = CONT2 + + SPRITE 24, 192, 96, (c AND $80) / 128 * 2, 0 + SPRITE 25, 192, 104, (c AND $40) / 64 * 2, 0 + SPRITE 26, 192, 112, (c AND $20) / 32 * 2, 0 + SPRITE 27, 192, 120, (c AND $10) / 16 * 2, 0 + SPRITE 28, 192, 128, (c AND $08) / 8 * 2, 0 + SPRITE 29, 192, 136, (c AND $04) / 4 * 2, 0 + SPRITE 30, 192, 144, (c AND $02) / 2 * 2, 0 + SPRITE 31, 192, 152, (c AND $01) / 1 * 2, 0 + + c = CONT2.KEY + + PRINT AT 847,<>c," " + + GOTO game_loop + + CHRROM 0 + + CHRROM PATTERN 0 + + BITMAP "..3333.." + BITMAP ".33..33." + BITMAP ".33..33." + BITMAP ".33..33." + BITMAP ".33..33." + BITMAP ".33..33." + BITMAP ".33..33." + BITMAP "..3333.." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "...33..." + BITMAP "..333..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP "..3333.." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + diff --git a/examples/demo.bas b/examples/demo.bas index 127efb7..c3f96f1 100644 --- a/examples/demo.bas +++ b/examples/demo.bas @@ -2,6 +2,7 @@ ' Demo of graphics for CVBasic ' ' by Oscar Toledo G. + ' https://nanochess.org/ ' ' Creation date: Mar/08/2024. ' Revision date: Aug/06/2024. Added endless loop. diff --git a/examples/demo_nes.bas b/examples/demo_nes.bas new file mode 100644 index 0000000..7e61325 --- /dev/null +++ b/examples/demo_nes.bas @@ -0,0 +1,708 @@ + ' + ' Demo of graphics for CVBasic + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Mar/08/2024. + ' Revision date: Aug/06/2024. Added endless loop. + ' Revision date: Aug/26/2025. Adapted for NES. + ' + + DIM x(32),y(32),col(32),col2(32) + + PALETTE LOAD demo_palette + + WHILE 1 + + CLS + + SCREEN portrait_pattern,0,260,8,8,8 + + GOSUB small_wait + + RESTORE message_1 + + GOSUB show_message + + GOSUB small_wait + + FOR c = 0 TO 7 + WAIT + WAIT + #c = 260 + c * 32 + SCREEN spaces,0,#c,8,1 + SCREEN portrait_pattern,0,#c+32,8,8,8 + NEXT c + + FOR c = 0 TO 7 + WAIT + WAIT + #c = 516 + c + SCREEN spaces,0,#c,1,8,1 + SCREEN portrait_pattern,0,#c+1,8,8,8 + NEXT c + + RESTORE message_2 + + GOSUB show_message + + GOSUB small_wait + + FOR c = 0 TO 144 + WAIT + SPRITE 0,$A0,c,$01,0 + SPRITE 1,$A0,c+8,$03,0 + NEXT c + + RESTORE message_3 + + GOSUB show_message + + GOSUB small_wait + + FOR c = 0 TO 31 + x(c) = RANDOM(176) + y(c) = RANDOM(240) + col(c) = c % 3 * 4 + col2(c) = (c / 3) % 4 + SPRITE c * 2,y(c),x(c),col(c) + 1,col2(c) + SPRITE c * 2 + 1,y(c),x(c) + 8,col(c) + 3,col2(c) + NEXT c + + FOR c = 0 TO 240 + WAIT + FOR d = 0 TO 15 + SPRITE d * 2,y(d),x(d),col(d) + 1,col2(d) + SPRITE d * 2 + 1,y(d),x(d) + 8,col(d) + 3,col2(d) + y(d) = y(d) + 1 + IF y(d) = $c0 THEN y(d) = $f1 + NEXT d + FOR d = 16 TO 31 + SPRITE d * 2,y(d),x(d),col(d) + 1,col2(d) + SPRITE d * 2 + 1,y(d),x(d) + 8,col(d) + 3,col2(d) + x(d) = x(d) + 1 + IF x(d) = $ff THEN x(d) = $00 + NEXT d + NEXT c + + FOR c = 0 TO 63 + SPRITE c,$f0,0,0,0 + NEXT c + + RESTORE message_4 + + GOSUB show_message + + GOSUB small_wait + + WEND + +demo_palette: + DATA BYTE $0F,$26,$27,$30 + DATA BYTE $0F,$26,$27,$30 + DATA BYTE $0F,$26,$27,$30 + DATA BYTE $0F,$26,$27,$30 + DATA BYTE $0F,$21,$22,$23 + DATA BYTE $0F,$24,$25,$26 + DATA BYTE $0F,$27,$28,$29 + DATA BYTE $0F,$2A,$2B,$2C + +spaces: + DATA BYTE $20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00 + +small_wait: PROCEDURE + FOR c = 0 TO 60 + WAIT + NEXT c + END + +show_message: PROCEDURE + + ' + ' Erase area. + ' + FOR #position = $210e TO $21ee STEP 32 + FOR #c = 0 TO $11 + VPOKE #position + #c, 32 + NEXT #c + NEXT #position + + ' + ' Draw message. + ' + #position = $2000 + 270 + DO + READ BYTE d + IF d = $0D THEN + #position = ((#position + 32) AND $FFe0) + $000e + ELSEIF d <> 0 THEN + VPOKE #position, d + #position = #position + 1 + END IF + WAIT + LOOP WHILE d + + END + +message_1: + DATA BYTE "Hi! This is a",$0d + DATA BYTE "CVBasic demo",$0d + DATA BYTE "program.",$00 + +message_2: + DATA BYTE "You can displace",$0d + DATA BYTE "graphics moving",$0d + DATA BYTE "tiles on the",$0d + DATA BYTE "screen instead of",$0d + DATA BYTE "graphic data.",$00 + +message_3: + DATA BYTE "Sprites can",$0d + DATA BYTE "overlay",$0d + DATA BYTE "background",$0d + DATA BYTE "graphics.",$00 + +message_4: + DATA BYTE "I hope you",$0d + DATA BYTE "enjoyed the",$0d + DATA BYTE "demo.",$00 + + CHRROM 0 PATTERN 256 + + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + BITMAP "1111111111111111" + + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + + ' TMSColor 3.2 Aug/26/2025 + ' Command: ../tmscolor/tmscolor -nes -t128 -b portrait_nes.png portrait_nes.bas + ' Created: Tue Aug 26 10:55:48 2025 + + ' Start tile = 128. Total_tiles = 52 + CHRROM 0 PATTERN 128 + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000111" + BITMAP "00111111" + BITMAP "11111111" + BITMAP "11111110" + BITMAP "11001100" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "11100000" + BITMAP "11100000" + BITMAP "11110000" + BITMAP "11110000" + BITMAP "11111000" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000002" + BITMAP "00000022" + BITMAP "00000222" + BITMAP "00002222" + BITMAP "00022222" + + BITMAP "00222222" + BITMAP "02222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "00000000" + BITMAP "20000000" + BITMAP "22200000" + BITMAP "22220000" + BITMAP "22222000" + BITMAP "22222200" + BITMAP "22222220" + BITMAP "22222220" + + BITMAP "11101110" + BITMAP "11101110" + BITMAP "00000000" + BITMAP "11101110" + BITMAP "11101110" + BITMAP "00000000" + BITMAP "11101110" + BITMAP "11101110" + + BITMAP "10011111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11000000" + BITMAP "10011111" + BITMAP "11111111" + BITMAP "11111110" + BITMAP "11001000" + + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + + BITMAP "00222222" + BITMAP "00222222" + BITMAP "02222222" + BITMAP "02222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22322222" + + BITMAP "22222220" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "20000000" + BITMAP "20000000" + BITMAP "20000000" + BITMAP "20000000" + BITMAP "22000000" + + BITMAP "00000000" + BITMAP "11101110" + BITMAP "11101110" + BITMAP "00000000" + BITMAP "11101110" + BITMAP "11101110" + BITMAP "00000000" + BITMAP "11101110" + + BITMAP "11010111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "11110000" + BITMAP "11000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333332" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "32223222" + BITMAP "32233222" + BITMAP "32233322" + BITMAP "21000000" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "21010222" + BITMAP "10101222" + + BITMAP "22000000" + BITMAP "22000000" + BITMAP "22000000" + BITMAP "22000000" + BITMAP "22000000" + BITMAP "22000000" + BITMAP "22000000" + BITMAP "22000000" + + BITMAP "11101110" + BITMAP "00000000" + BITMAP "11101110" + BITMAP "11101110" + BITMAP "00000000" + BITMAP "11101110" + BITMAP "11101110" + BITMAP "00000000" + + BITMAP "00000000" + BITMAP "00011111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11110001" + BITMAP "10001111" + BITMAP "11111111" + + BITMAP "00000000" + BITMAP "11110000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + + BITMAP "32200000" + BITMAP "22211111" + BITMAP "22211111" + BITMAP "22211111" + BITMAP "22211111" + BITMAP "22211111" + BITMAP "22221111" + BITMAP "22221111" + + BITMAP "11111111" + BITMAP "11111111" + BITMAP "01111111" + BITMAP "01111111" + BITMAP "01111111" + BITMAP "01111111" + BITMAP "01111111" + BITMAP "00111111" + + BITMAP "11010222" + BITMAP "10101222" + BITMAP "11010222" + BITMAP "11101222" + BITMAP "11010222" + BITMAP "11101222" + BITMAP "11010222" + BITMAP "11101222" + + BITMAP "11111101" + BITMAP "10000001" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11000000" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11100000" + BITMAP "10000000" + + BITMAP "22222111" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222221" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "32222222" + + BITMAP "11111111" + BITMAP "11111111" + BITMAP "00000111" + BITMAP "00000111" + BITMAP "10001111" + BITMAP "11111100" + BITMAP "21111100" + BITMAP "22111100" + + BITMAP "11010222" + BITMAP "11101222" + BITMAP "11010222" + BITMAP "10100222" + BITMAP "11000222" + BITMAP "01001223" + BITMAP "00010223" + BITMAP "01101233" + + BITMAP "11111100" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "32222222" + BITMAP "32222222" + BITMAP "32222222" + BITMAP "32222222" + BITMAP "32222222" + BITMAP "32222222" + BITMAP "32222222" + BITMAP "32222222" + + BITMAP "22111111" + BITMAP "22200000" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22221211" + BITMAP "00000011" + BITMAP "02020211" + + BITMAP "01010233" + BITMAP "10101233" + BITMAP "01010233" + BITMAP "10101233" + BITMAP "11010232" + BITMAP "11101232" + BITMAP "11111232" + BITMAP "11111232" + + BITMAP "22000000" + BITMAP "22000000" + BITMAP "22000000" + BITMAP "22000000" + BITMAP "22000000" + BITMAP "22200000" + BITMAP "22200000" + BITMAP "22200000" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000003" + BITMAP "00003333" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000030" + BITMAP "00003333" + BITMAP "00333333" + BITMAP "03333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "32222222" + BITMAP "32222222" + BITMAP "32222222" + BITMAP "32222220" + BITMAP "32222220" + BITMAP "32222223" + BITMAP "32222233" + BITMAP "32222233" + + BITMAP "00202011" + BITMAP "20020201" + BITMAP "20202000" + BITMAP "02020233" + BITMAP "20202033" + BITMAP "02020233" + BITMAP "20202033" + BITMAP "02020232" + + BITMAP "11111232" + BITMAP "11111222" + BITMAP "11111222" + BITMAP "11111222" + BITMAP "00000222" + BITMAP "02020222" + BITMAP "20200222" + BITMAP "02020222" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22223333" + BITMAP "22333333" + + BITMAP "22200000" + BITMAP "22333333" + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "33300000" + BITMAP "33333000" + BITMAP "33333300" + BITMAP "33333330" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "30303030" + BITMAP "03030303" + BITMAP "30303030" + BITMAP "03030303" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "03333333" + BITMAP "30333333" + BITMAP "03333333" + BITMAP "33333333" + BITMAP "03333333" + + BITMAP "32222233" + BITMAP "33222233" + BITMAP "33333330" + BITMAP "33333332" + BITMAP "30303030" + BITMAP "03030302" + BITMAP "30303020" + BITMAP "03030202" + + BITMAP "20202320" + BITMAP "02020202" + BITMAP "20202020" + BITMAP "02020202" + BITMAP "20202020" + BITMAP "02020202" + BITMAP "20202023" + BITMAP "02020233" + + BITMAP "20202022" + BITMAP "02020022" + BITMAP "20203333" + BITMAP "02033333" + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "22333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333303" + BITMAP "33333030" + BITMAP "33030303" + BITMAP "30303030" + BITMAP "03030303" + + BITMAP "33333033" + BITMAP "33030303" + BITMAP "30303030" + BITMAP "03030303" + BITMAP "30303030" + BITMAP "03030303" + BITMAP "30303030" + BITMAP "03030303" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "03333333" + BITMAP "30333333" + BITMAP "03033333" + BITMAP "30303333" + BITMAP "03033333" + + + ' Width = 8, height = 8 +portrait_pattern: + DATA BYTE $80,$81,$82,$83,$84,$85,$86,$86 + DATA BYTE $87,$88,$89,$84,$84,$8a,$8b,$8c + DATA BYTE $8d,$8e,$8f,$90,$91,$84,$92,$93 + DATA BYTE $94,$95,$96,$97,$98,$84,$92,$86 + DATA BYTE $99,$9a,$9b,$9c,$9d,$84,$92,$8c + DATA BYTE $9e,$9f,$a0,$a1,$a2,$84,$a3,$93 + DATA BYTE $a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab + DATA BYTE $ac,$ad,$ae,$af,$b0,$b1,$b2,$b3 diff --git a/examples/demo_sms.bas b/examples/demo_sms.bas new file mode 100644 index 0000000..2461634 --- /dev/null +++ b/examples/demo_sms.bas @@ -0,0 +1,412 @@ + ' + ' Demo of graphics for CVBasic + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Mar/08/2024. + ' Revision date: Aug/06/2024. Added endless loop. + ' Revision date: Apr/27/2025. Adapted for Sega Master System. + ' + + DIM x(32),y(32),col(32) + + MODE 4 + + DEFINE SPRITE 0,28,sprite_bitmap + + ' Make a bold font + FOR #c = $0400 TO $0FFF + d = VPEEK(#c) + VPOKE #c, d OR (d + d) + NEXT #c + FOR #c = $0403 TO $0FFF STEP 4 + VPOKE #c, 0 + NEXT #c + + DEFINE CHAR 128,62,portrait_char + + WHILE 1 + + CLS + + SCREEN portrait_pattern,0,260,8,8,8 + + GOSUB small_wait + + RESTORE message_1 + + GOSUB show_message + + GOSUB small_wait + + FOR c = 0 TO 7 + WAIT + WAIT + #c = 260 + c * 32 + SCREEN spaces,0,#c,8,1 + SCREEN portrait_pattern,0,#c+32,8,8,8 + NEXT c + + FOR c = 0 TO 7 + WAIT + WAIT + #c = 516 + c + SCREEN spaces,0,#c,1,8,1 + SCREEN portrait_pattern,0,#c+1,8,8,8 + NEXT c + + RESTORE message_2 + + GOSUB show_message + + GOSUB small_wait + + FOR c = 0 TO 144 + WAIT + SPRITE 0,$A0,c,$00 + SPRITE 1,$A0,c+8,$02 + NEXT c + + RESTORE message_3 + + GOSUB show_message + + GOSUB small_wait + + FOR c = 0 TO 31 + x(c) = RANDOM(176) + y(c) = RANDOM(240) + col(c) = (c % 14) * 4 + SPRITE c * 2,y(c),x(c),col(c) + SPRITE c * 2 + 1,y(c),x(c) + 8,col(c) + 2 + NEXT c + + FOR c = 0 TO 240 + WAIT + FOR d = 0 TO 15 + SPRITE d * 2,y(d),x(d),col(d) + SPRITE d * 2 + 1,y(d),x(d) + 8,col(d) + 2 + y(d) = y(d) + 1 + IF y(d) = $c0 THEN y(d) = $f1 + NEXT d + FOR d = 16 TO 31 + SPRITE d * 2,y(d),x(d),col(d) + SPRITE d * 2 + 1,y(d),x(d) + 8,col(d) + 2 + x(d) = x(d) + 1 + IF x(d) = $ff THEN x(d) = $00 + NEXT d + NEXT c + + FOR c = 0 TO 63 + SPRITE c,$E0,0,0 + NEXT c + + RESTORE message_4 + + GOSUB show_message + + GOSUB small_wait + + WEND + +spaces: + DATA BYTE $20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00,$20,$00 + + INCLUDE "portrait_sms.bas" + +small_wait: PROCEDURE + FOR c = 0 TO 60 + WAIT + NEXT c + END + +show_message: PROCEDURE + + ' + ' Erase area. + ' + FOR #position = $3A1C TO $3BDC STEP 64 + FOR #c = 0 TO $22 STEP 2 + VPOKE #position + #c, 32 + NEXT #c + NEXT #position + + ' + ' Draw message. + ' + #position = $3800 + 270 * 2 + DO + READ BYTE d + IF d = $0D THEN + #position = ((#position + 64) AND $FFC0) + $001C + ELSEIF d <> 0 THEN + VPOKE #position, d + #position = #position + 2 + END IF + WAIT + LOOP WHILE d + + END + +message_1: + DATA BYTE "Hi! This is a",$0d + DATA BYTE "CVBasic demo",$0d + DATA BYTE "program.",$00 + +message_2: + DATA BYTE "You can displace",$0d + DATA BYTE "graphics moving",$0d + DATA BYTE "tiles on the",$0d + DATA BYTE "screen instead of",$0d + DATA BYTE "graphic data.",$00 + +message_3: + DATA BYTE "Sprites can",$0d + DATA BYTE "overlay",$0d + DATA BYTE "background",$0d + DATA BYTE "graphics.",$00 + +message_4: + DATA BYTE "I hope you",$0d + DATA BYTE "enjoyed the",$0d + DATA BYTE "demo.",$00 + +sprite_bitmap: + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + BITMAP "2222222222222222" + + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + BITMAP "4444444444444444" + + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + BITMAP "5555555555555555" + + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + BITMAP "6666666666666666" + + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + BITMAP "7777777777777777" + + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + BITMAP "8888888888888888" + + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + BITMAP "9999999999999999" + + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + BITMAP "BBBBBBBBBBBBBBBB" + + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + BITMAP "CCCCCCCCCCCCCCCC" + + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + BITMAP "DDDDDDDDDDDDDDDD" + + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + BITMAP "EEEEEEEEEEEEEEEE" + + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" + BITMAP "FFFFFFFFFFFFFFFF" diff --git a/examples/face_joystick_nes.bas b/examples/face_joystick_nes.bas new file mode 100644 index 0000000..e7f383a --- /dev/null +++ b/examples/face_joystick_nes.bas @@ -0,0 +1,73 @@ + ' + ' Joystick moving with joystick (demo for CVBasic) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Mar/03/2024. + ' Revision date: Aug/23/2025. Adapted for NES. + ' + +' DEFINE SPRITE 0,1,happy_face + +' PRINT AT 36,"Happy face!" + + PALETTE LOAD full_palette + + x = 50 + y = 100 + + SPRITE 2, 96, 128, 1, 3 + SPRITE 3, 96, 136, 3, 3 + +game_loop: + WAIT + PALETTE 31, (FRAME / 16) + $30 + +' PRINT AT 0, "VDP.STATUS = ", <>VDP.STATUS , " " + + SPRITE 0, y - 1, x, 1, face_palette + SPRITE 1, y - 1, x + 8, 3, face_palette + + IF FRAME AND 1 THEN + face_palette = 0 + IF cont1.up THEN IF y > 0 THEN y = y - 1 + IF cont1.left THEN IF x > 0 THEN x = x - 1 + IF cont1.right THEN IF x < 240 THEN x = x + 1 + IF cont1.down THEN IF y < 176 THEN y = y + 1 + IF cont1.button THEN face_palette = 1 + IF cont1.button2 THEN face_palette = 2 + END IF + + GOTO game_loop + +full_palette: + DATA BYTE $0F,$30,$30,$30 + DATA BYTE $0F,$30,$30,$30 + DATA BYTE $0F,$30,$30,$30 + DATA BYTE $0F,$30,$30,$30 + DATA BYTE $0F,$30,$30,$32 + DATA BYTE $0F,$30,$30,$34 + DATA BYTE $0F,$30,$30,$36 + DATA BYTE $0F,$30,$30,$38 + + CHRROM 0 + + CHRROM PATTERN 256 + + BITMAP "......3333......" + BITMAP "....33333333...." + BITMAP "...3333333333..." + BITMAP "..333333333333.." + BITMAP ".33333333333333." + BITMAP ".33333333333333." + BITMAP "3333..3333..3333" + BITMAP "3333..3333..3333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP ".33333333333333." + BITMAP ".333..3333..333." + BITMAP "..333......333.." + BITMAP "...333....333..." + BITMAP "....33333333...." + BITMAP "......3333......" diff --git a/examples/face_joystick_sms.bas b/examples/face_joystick_sms.bas new file mode 100644 index 0000000..52dc4ec --- /dev/null +++ b/examples/face_joystick_sms.bas @@ -0,0 +1,65 @@ + ' + ' Joystick moving with joystick (demo for CVBasic) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Mar/03/2024. + ' Revision date: Apr/26/2025. Adapted for Sega Master System. + ' + + DEFINE SPRITE 0,2,happy_face + + PRINT AT 36,"Happy face!" + + x = 50 + y = 100 + + SPRITE 2, 96, 128, 0 + SPRITE 3, 96, 136, 2 + +game_loop: + WAIT + PRINT AT 0, "VDP.STATUS = ", <>VDP.STATUS , " " + + SPRITE 0,y-1,x,0 + SPRITE 1,y-1,x+8,2 + + IF FRAME AND 1 THEN + IF cont1.up THEN IF y > 0 THEN y = y - 1 + IF cont1.left THEN IF x > 0 THEN x = x - 1 + IF cont1.right THEN IF x < 240 THEN x = x + 1 + IF cont1.down THEN IF y < 176 THEN y = y + 1 + IF cont1.button THEN + PALETTE 10+16,$30 + ELSEIF cont1.button2 THEN + PALETTE 10+16,$03 + ELSE + PALETTE 10+16,$0F + END IF + END IF + + GOTO game_loop + + ' The face includes the color for the sprite + ' A = Yellow in the default palette. + ' F = White + ' 4 = Yellow + ' 6 = Red +happy_face: + BITMAP "......AAAA......" + BITMAP "....AAAAAAAA...." + BITMAP "...AAAAAAAAAA..." + BITMAP "..AAAAAAAAAAAA.." + BITMAP ".AAAAAAAAAAAAAA." + BITMAP ".AAFFFAAAAFFFAA." + BITMAP "AAAFFFAAAAFFFAAA" + BITMAP "AAAFF4AAAA4FFAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP ".AAAAAAAAAAAAAA." + BITMAP ".AAA66AAAA66AAA." + BITMAP "..AAA666666AAA.." + BITMAP "...AAA6666AAA..." + BITMAP "....AAAAAAAA...." + BITMAP "......AAAA......" diff --git a/examples/fm_instruments.bas b/examples/fm_instruments.bas new file mode 100644 index 0000000..037e06f --- /dev/null +++ b/examples/fm_instruments.bas @@ -0,0 +1,394 @@ + ' + ' Test of FM instruments + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Jul/14/2026. + ' + + ' Enable ASCII16 or Sega Mapper in emulation. + + BANK ROM 128 ' Required to enable FM. + + OPTION FM ON ' Required to integrate FM support. + + ' + ' This requires a MSX2+ or Sega Master System with FM extension (Japanese) + ' + PLAY INSTRUMENT $00,$00,$00,$00 ' All voices using user-defined instrument. + + debounce = 15 + + instrument = 0 + + PLAY FULL + + IF MUSIC.FM_ENABLED THEN + PRINT AT 2,"Ready to play FM..." + END IF + +game_loop: + PRINT AT 66,"Instrument: ",<2>instrument + DEFINE VRAM $1866, 24, VARPTR names(instrument * 24) + PRINT AT 162,"Press button to play" + + WAIT + c = CONT + IF debounce THEN debounce = debounce - 1: c = 0 + IF c AND 1 THEN + debounce = 15 + IF instrument = 0 THEN instrument = 63 ELSE instrument = instrument - 1 + END IF + IF c AND 4 THEN + debounce = 15 + IF instrument = 63 THEN instrument = 0 ELSE instrument = instrument + 1 + END IF + IF c AND 64 THEN + debounce = 15 + PLAY INSTRUMENT VARPTR fm_instruments(instrument * 8) ' Define instrument. + PLAY music_simple + END IF + IF c AND 128 THEN + debounce = 15 + PLAY INSTRUMENT VARPTR fm_instruments(instrument * 8) ' Define instrument. + PLAY music_bach + END IF + GOTO game_loop + +music_simple: + DATA BYTE 16 + MUSIC C4,-,-,- + MUSIC D4,-,-,- + MUSIC E4,-,-,- + MUSIC F4,-,-,- + MUSIC G4,-,-,- + MUSIC A4,-,-,- + MUSIC B4,-,-,- + MUSIC C5,-,-,- + MUSIC -,-,-,- + MUSIC STOP + + ' Bach Invention 8 (BWV779) + ' Fragment +music_bach: DATA BYTE 7 + MUSIC F4,- + MUSIC S,- + MUSIC A4,- + MUSIC S,- + MUSIC F4,- + MUSIC S,- + MUSIC C5,- + MUSIC S,- + MUSIC F4,- + MUSIC S,- + + MUSIC F5,- + MUSIC S,- + MUSIC E5,F3 + MUSIC D5,S + MUSIC C5,A3 + MUSIC D5,S + MUSIC C5,F3 + MUSIC A4#,S + MUSIC A4,C4 + MUSIC A4#,S + MUSIC A4,F3 + MUSIC G4,S + + MUSIC F4,F4 + MUSIC S,S + MUSIC A4,E4 + MUSIC S,D4 + MUSIC C5,C4 + MUSIC S,D4 + MUSIC A4,C4 + MUSIC S,A3# + MUSIC F5,A3 + MUSIC S,A3# + MUSIC C5,A3 + MUSIC S,G3 + + MUSIC A5,F3 + MUSIC C6,S + MUSIC A5#,A3 + MUSIC C6,S + MUSIC A5,C4 + MUSIC C6,S + MUSIC A5#,A3 + MUSIC C6,S + MUSIC A5,F4 + MUSIC C6,S + MUSIC A5#,C4 + MUSIC C6,S + + MUSIC F5,A3 + MUSIC A5,C4 + MUSIC G5,A3# + MUSIC A5,C4 + MUSIC F5,A3 + MUSIC A5,C4 + MUSIC G5,A3# + MUSIC A5,C4 + MUSIC F5,A3 + MUSIC A5,C4 + MUSIC G5,A3# + MUSIC A5,C4 + + MUSIC D5,F3 + MUSIC F5,A3 + MUSIC E5,G3 + MUSIC F5,A3 + MUSIC D5,F3 + MUSIC F5,A3 + MUSIC E5,G3 + MUSIC F5,A3 + MUSIC D5,F3 + MUSIC F5,A3 + MUSIC E5,G3 + MUSIC F5,A3 + + MUSIC B4,D3 + MUSIC S,F3 + MUSIC G4,E3 + MUSIC S,F3 + MUSIC D5,D3 + MUSIC S,F3 + MUSIC B4,E3 + MUSIC S,F3 + MUSIC F5,D3 + MUSIC S,F3 + MUSIC D5,E3 + MUSIC S,F3 + + MUSIC G5,B3 + MUSIC A5,S + MUSIC G5,G3 + MUSIC F5,S + MUSIC E5,C4 + MUSIC F5,S + MUSIC E5,G3 + MUSIC D5,S + MUSIC C5,E4 + MUSIC D5,S + MUSIC C5,C4 + MUSIC A4#,S + + MUSIC A4,F4 + MUSIC S,G4 + MUSIC D5,F4 + MUSIC C5,E4 + MUSIC B4,D4 + MUSIC C5,E4 + MUSIC B4,D4 + MUSIC A4,C4 + MUSIC G4,B3 + MUSIC A4,C4 + MUSIC G4,B3 + MUSIC F4,A3 + + MUSIC E4,G3 + MUSIC F4,S + MUSIC E4,C4 + MUSIC D4,B3 + MUSIC C4,A3 + MUSIC S,B3 + MUSIC C5,A3 + MUSIC B4,G3 + MUSIC C5,F3 + MUSIC S,G3 + MUSIC E4,F3 + MUSIC S,E3 + + MUSIC F4,D3 + MUSIC S,E3 + MUSIC C5,D3 + MUSIC S,C3 + MUSIC E4,G3 + MUSIC S,F3 + MUSIC C5,E3 + MUSIC S,F3 + MUSIC D4,G3 + MUSIC S,S + MUSIC B4,G2 + MUSIC S,S + + MUSIC C5,C4 + MUSIC S,S + MUSIC S,S + MUSIC S,S + MUSIC STOP + + ' + ' Editing waveforms (an editor could be made): + ' + ' Register: + ' 7 6 5 4 3 2 1 0 + ' 0 AM(M) VIB(M) EGT(M) KSR(M) Multiple(M) + ' 1 AM(C) VIB(C) EGT(C) KSR(C) Multiple(C) + ' 2 KSL(M) Total LEVEL MODULATOR + ' 3 KSL(C) Unused DC DM Feedback + ' 4 Attack (M) Decay (M) + ' 5 Attack (C) Decay (C) + ' 6 Sustain (M) Release (M) + ' 7 Sustain (C) Release (C) + ' + + ' + ' Extracted from the FM-PAC ROM for Sony HB-F1XDJ by nanochess. Jul/14/2026. + ' + ' The original ROM entries are 32-byte wide, I ran an automatic processor + ' to get the following data in register order. + ' + ' Register: + ' 0 = rom[16] + ' 1 = rom[24] + ' 2 = rom[17] + ' 3 = (rom[25] & ~0x07) | ((rom[10] >> 1) & 7) Feedback (preserves KSL(C)) + ' 4 = rom[18] + ' 5 = rom[26] + ' 6 = rom[19] + ' 7 = rom[27] + ' + ' The ROM also contains entries 8-byte wide, but these have a + ' fixed 0x20 value in byte 3, and this renders it unusable. + ' + ' This data is important as there aren't references of instruments + ' settings for YM2413 in Internet (not even in the datasheet). + ' + ' Probably all original Japanese composing software for MSX2+ + ' referred to the instruments ROM or allowed some alteration on + ' values, but it was early 1990s and without Internet probably + ' this data was forgotten. + ' +fm_instruments: + DATA BYTE $31, $11, $0e, $05, $d9, $b2, $11, $f4 ' 0: Piano 1 + DATA BYTE $30, $10, $0f, $04, $d9, $b2, $10, $f3 ' 1: Piano 2 + DATA BYTE $61, $61, $12, $07, $b4, $56, $14, $17 ' 2: Violin + DATA BYTE $61, $31, $20, $07, $6c, $43, $18, $26 ' 3: Flute 1 + DATA BYTE $a2, $30, $a0, $07, $88, $54, $14, $06 ' 4: Clarinet + DATA BYTE $31, $34, $20, $05, $72, $56, $0a, $1c ' 5: Oboe + DATA BYTE $31, $71, $16, $07, $51, $52, $26, $24 ' 6: Trumpet + DATA BYTE $34, $30, $37, $06, $50, $30, $76, $06 ' 7: Pipe organ + DATA BYTE $17, $52, $18, $05, $88, $d9, $66, $24 ' 8: Xylophone + DATA BYTE $e1, $63, $0a, $06, $fc, $f8, $28, $29 ' 9: Organ + DATA BYTE $02, $41, $15, $05, $a3, $a3, $75, $05 ' 10: Guitar + DATA BYTE $19, $53, $0c, $06, $c7, $f5, $11, $03 ' 11: Santool 1 + DATA BYTE $23, $43, $0f, $07, $dd, $bf, $4a, $05 ' 12: Electric piano 1 + DATA BYTE $03, $09, $11, $0e, $d2, $b4, $f4, $f5 ' 13: Clavicode 1 + DATA BYTE $01, $00, $06, $1e, $a3, $e2, $f4, $f4 ' 14: Harpsicode 1 + DATA BYTE $01, $01, $11, $0e, $c0, $b4, $01, $f6 ' 15: Harpsicode 2 + DATA BYTE $f9, $f1, $24, $06, $95, $d1, $e5, $f2 ' 16: Vibraphone + DATA BYTE $13, $11, $0c, $06, $fc, $d2, $33, $83 ' 17: Koto 1 + DATA BYTE $01, $10, $0e, $07, $ca, $e6, $44, $24 ' 18: Taiko + DATA BYTE $e0, $f4, $1b, $87, $11, $f0, $04, $08 ' 19: Engine 1 + DATA BYTE $ff, $70, $19, $07, $50, $1f, $05, $01 ' 20: UFO + DATA BYTE $13, $11, $11, $07, $fa, $f2, $21, $f4 ' 21: Synthesizer bell + DATA BYTE $a6, $42, $10, $0d, $fb, $b9, $11, $02 ' 22: Chime + DATA BYTE $40, $31, $89, $06, $c7, $f9, $14, $04 ' 23: Synthesizer bass + DATA BYTE $42, $44, $0b, $06, $94, $b0, $33, $f6 ' 24: Synthesizer + DATA BYTE $01, $03, $0b, $07, $ba, $d9, $25, $06 ' 25: Synthesizer percussion + DATA BYTE $40, $00, $00, $07, $fa, $d9, $37, $04 ' 26: Synthesizer rhythm + DATA BYTE $02, $03, $09, $07, $cb, $ff, $39, $06 ' 27: Harm drum + DATA BYTE $18, $11, $09, $05, $f8, $f5, $26, $26 ' 28: Cowbell + DATA BYTE $0b, $04, $09, $07, $f0, $f5, $01, $27 ' 29: Close hi-hat + DATA BYTE $40, $40, $07, $07, $d0, $d6, $01, $27 ' 30: Snare drum + DATA BYTE $00, $01, $07, $06, $cb, $e3, $36, $25 ' 31: Bass drum + DATA BYTE $11, $11, $08, $04, $fa, $b2, $20, $f4 ' 32: Piano 3 + DATA BYTE $11, $11, $11, $00, $c0, $b2, $01, $f4 ' 33: Electric piano 2 + DATA BYTE $19, $53, $15, $07, $e7, $95, $21, $03 ' 34: Santool 2 + DATA BYTE $30, $70, $19, $07, $42, $62, $26, $24 ' 35: Brass + DATA BYTE $62, $71, $25, $07, $64, $43, $12, $26 ' 36: Flute 2 + DATA BYTE $21, $03, $0b, $05, $90, $d4, $02, $f5 ' 37: Clavicode 2 + DATA BYTE $01, $03, $0a, $05, $90, $a4, $03, $f5 ' 38: Clavicode 3 + DATA BYTE $43, $53, $0e, $85, $b5, $e9, $84, $04 ' 39: Koto 2 + DATA BYTE $34, $30, $26, $06, $50, $30, $76, $06 ' 40: Pipe organ 2 + DATA BYTE $73, $33, $5a, $06, $99, $f5, $14, $15 ' 41: PohdsPLA + DATA BYTE $73, $13, $16, $05, $f9, $f5, $33, $03 ' 42: RohdsPRA + DATA BYTE $61, $21, $15, $07, $76, $54, $23, $06 ' 43: Orch L + DATA BYTE $63, $70, $1b, $07, $75, $4b, $45, $15 ' 44: Orch R + DATA BYTE $61, $a1, $0a, $05, $76, $54, $12, $07 ' 45: Synthesizer violin + DATA BYTE $61, $78, $0d, $0d, $85, $f2, $14, $03 ' 46: Synthesizer organ + DATA BYTE $31, $71, $15, $07, $b6, $f9, $03, $26 ' 47: Synthesizer bass + DATA BYTE $61, $71, $0d, $05, $75, $f2, $18, $03 ' 48: Tube + DATA BYTE $03, $0c, $14, $06, $a7, $fc, $13, $15 ' 49: Shamisen + DATA BYTE $13, $32, $80, $03, $20, $85, $03, $af ' 50: Magical + DATA BYTE $f1, $31, $17, $05, $23, $40, $14, $09 ' 51: Huwawa + DATA BYTE $f0, $74, $17, $47, $5a, $43, $06, $fc ' 52: Wander flat + DATA BYTE $20, $71, $0d, $06, $c1, $d5, $56, $06 ' 53: Hardrock + DATA BYTE $30, $32, $06, $06, $40, $40, $04, $74 ' 54: Machine + DATA BYTE $30, $32, $03, $03, $40, $40, $04, $74 ' 55: Machine V + DATA BYTE $01, $08, $0d, $07, $78, $f8, $7f, $f9 ' 56: Comic + DATA BYTE $c8, $c0, $0b, $05, $76, $f7, $11, $f9 ' 57: SE-Comic + DATA BYTE $49, $40, $0b, $07, $b4, $f9, $ff, $05 ' 58: SE-Laser + DATA BYTE $cd, $42, $0c, $06, $a2, $f0, $00, $01 ' 59: SE-Noise + DATA BYTE $51, $42, $13, $07, $13, $10, $42, $01 ' 60: SE-Star 1 + DATA BYTE $51, $42, $13, $07, $13, $10, $42, $01 ' 61: SE-Star 2 + DATA BYTE $30, $34, $12, $06, $23, $70, $26, $02 ' 62: Engine 2 + DATA BYTE $00, $00, $ff, $f8, $00, $00, $ff, $ff ' 63: Silence + +names: + DATA BYTE " Piano 1" + DATA BYTE " Piano 2" + DATA BYTE " Violin" + DATA BYTE " Flute 1" + DATA BYTE " Clarinet" + DATA BYTE " Oboe" + DATA BYTE " Trumpet" + DATA BYTE " Pipe organ" + DATA BYTE " Xylophone" + DATA BYTE " Organ" + DATA BYTE " Guitar" + DATA BYTE " Santool 1" + DATA BYTE " Electric piano 1" + DATA BYTE " Clavicode 1" + DATA BYTE " Harpsicode 1" + DATA BYTE " Harpsicode 2" + DATA BYTE " Vibraphone" + DATA BYTE " Koto 1" + DATA BYTE " Taiko" + DATA BYTE " Engine 1" + DATA BYTE " UFO" + DATA BYTE " Synthesizer bell" + DATA BYTE " Chime" + DATA BYTE " Synthesizer bass" + DATA BYTE " Synthesizer" + DATA BYTE " Synthesizer percussion" + DATA BYTE " Synthesizer rhythm" + DATA BYTE " Harm drum" + DATA BYTE " Cowbell" + DATA BYTE " Close hi-hat" + DATA BYTE " Snare drum" + DATA BYTE " Bass drum" + DATA BYTE " Piano 3" + DATA BYTE " Electric piano 2" + DATA BYTE " Santool 2" + DATA BYTE " Brass" + DATA BYTE " Flute 2" + DATA BYTE " Clavicode 2" + DATA BYTE " Clavicode 3" + DATA BYTE " Koto 2" + DATA BYTE " Pipe organ 2" + DATA BYTE " PohdsPLA" + DATA BYTE " RohdsPRA" + DATA BYTE " Orch L" + DATA BYTE " Orch R" + DATA BYTE " Synthesizer violin" + DATA BYTE " Synthesizer organ" + DATA BYTE " Synthesizer bass" + DATA BYTE " Tube" + DATA BYTE " Shamisen" + DATA BYTE " Magical" + DATA BYTE " Huwawa" + DATA BYTE " Wander flat" + DATA BYTE " Hardrock" + DATA BYTE " Machine" + DATA BYTE " Machine V" + DATA BYTE " Comic" + DATA BYTE " SE-Comic" + DATA BYTE " SE-Laser" + DATA BYTE " SE-Noise" + DATA BYTE " SE-Star 1" + DATA BYTE " SE-Star 2" + DATA BYTE " Engine 2" + DATA BYTE " Silence" \ No newline at end of file diff --git a/examples/happy_face_nes.bas b/examples/happy_face_nes.bas new file mode 100644 index 0000000..c4096d2 --- /dev/null +++ b/examples/happy_face_nes.bas @@ -0,0 +1,53 @@ + ' + ' Bouncing happy face (demo for CVBasic) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Feb/28/2024. + ' Revision date: Aug/23/2025. Adapted for NES. + ' + + PALETTE $13, $38 ' Yellow face + + PRINT AT 68,"Happy face!" + + x = 50 + y = 100 + dx = 1 + dy = 1 + +game_loop: + WAIT + WAIT + SPRITE 0,y-1,x,1,0 + SPRITE 1,y-1,x + 8,3,0 + + x = x + dx + IF x = 0 THEN dx = -dx + IF x = 240 THEN dx = -dx + y = y + dy + IF y = 0 THEN dy = -dy + IF y = 176 THEN dy = -dy + GOTO game_loop + + CHRROM 0 + + CHRROM PATTERN 256 + + BITMAP "......3333......" + BITMAP "....33333333...." + BITMAP "...3333333333..." + BITMAP "..333333333333.." + BITMAP ".33333333333333." + BITMAP ".33333333333333." + BITMAP "3333..3333..3333" + BITMAP "3333..3333..3333" + BITMAP "3333333333333333" + BITMAP "3333333333333333" + BITMAP ".33333333333333." + BITMAP ".333..3333..333." + BITMAP "..333......333.." + BITMAP "...333....333..." + BITMAP "....33333333...." + BITMAP "......3333......" diff --git a/examples/happy_face_sms.bas b/examples/happy_face_sms.bas new file mode 100644 index 0000000..cf7dded --- /dev/null +++ b/examples/happy_face_sms.bas @@ -0,0 +1,55 @@ + ' + ' Bouncing happy face (demo for CVBasic) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Feb/28/2024. + ' Revision date: Apr/26/2025. Adapted for Sega Master System. + ' + + DEFINE SPRITE 0,2,happy_face + + PRINT AT 36,"Happy face!" + + x = 50 + y = 100 + dx = 1 + dy = 1 + +game_loop: + WAIT + WAIT + SPRITE 0,y-1,x,0 + SPRITE 1,y-1,x+8,2 + + x = x + dx + IF x = 0 THEN dx = -dx + IF x = 240 THEN dx = -dx + y = y + dy + IF y = 0 THEN dy = -dy + IF y = 176 THEN dy = -dy + GOTO game_loop + + ' The face includes the color for the sprite + ' A = Yellow in the default palette. + ' F = White + ' 4 = Yellow + ' 6 = Red +happy_face: + BITMAP "......AAAA......" + BITMAP "....AAAAAAAA...." + BITMAP "...AAAAAAAAAA..." + BITMAP "..AAAAAAAAAAAA.." + BITMAP ".AAAAAAAAAAAAAA." + BITMAP ".AAFFFAAAAFFFAA." + BITMAP "AAAFFFAAAAFFFAAA" + BITMAP "AAAFF4AAAA4FFAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP "AAAAAAAAAAAAAAAA" + BITMAP ".AAAAAAAAAAAAAA." + BITMAP ".AAA66AAAA66AAA." + BITMAP "..AAA666666AAA.." + BITMAP "...AAA6666AAA..." + BITMAP "....AAAAAAAA...." + BITMAP "......AAAA......" diff --git a/examples/moving_faces.bas b/examples/moving_faces.bas new file mode 100644 index 0000000..22af1b5 --- /dev/null +++ b/examples/moving_faces.bas @@ -0,0 +1,64 @@ + ' + ' Multiple happy faces (demo for CVBasic) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Jun/25/2025. + ' + + DEFINE SPRITE 0,1,happy_face + + PRINT AT 36,"Happy faces!" + + SIGNED #x + + DIM #x(4) + DIM #y(4) + + ' The horizontal movement range for sprites is from -32 to 255 + + #x(0) = -32 + #y(0) = -8 + + #x(1) = 32 + #y(1) = 64 + + #x(2) = 64 + #y(2) = 128 + + #x(3) = 96 + #y(3) = 184 + +game_loop: + FOR c = 0 tO 3 + IF #x(c) < 0 THEN + ' The early clock bit moves the sprite 32 pixels to the left. + SPRITE c, #y(c) - 1, #x(c) + 32, 0, 10 + $80 + ELSE + SPRITE c, #y(c) - 1, #x(c), 0, 10 + END IF + #x(c) = #x(c) + 1 + IF #x(c) >= 256 THEN #x(c) = -32 + NEXT c + WAIT + WAIT + GOTO game_loop + +happy_face: + BITMAP "......XXXX......" + BITMAP "....XXXXXXXX...." + BITMAP "...XXXXXXXXXX..." + BITMAP "..XXXXXXXXXXXX.." + BITMAP ".XXXXXXXXXXXXXX." + BITMAP ".XXXXXXXXXXXXXX." + BITMAP "XXXX..XXXX..XXXX" + BITMAP "XXXX..XXXX..XXXX" + BITMAP "XXXXXXXXXXXXXXXX" + BITMAP "XXXXXXXXXXXXXXXX" + BITMAP ".XXXXXXXXXXXXXX." + BITMAP ".XXX..XXXX..XXX." + BITMAP "..XXX......XXX.." + BITMAP "...XXX....XXX..." + BITMAP "....XXXXXXXX...." + BITMAP "......XXXX......" diff --git a/examples/music.bas b/examples/music.bas index 7107917..642223f 100644 --- a/examples/music.bas +++ b/examples/music.bas @@ -32,7 +32,7 @@ play_1: repeat: WAIT IF CONT1.BUTTON2 THEN V=15 - SOUND 2,300,V +REM SOUND 2,300,V IF V>0 THEN V=V-1 IF MUSIC.PLAYING THEN GOTO repeat GOTO wait_key diff --git a/examples/music_fm.bas b/examples/music_fm.bas new file mode 100644 index 0000000..afa130d --- /dev/null +++ b/examples/music_fm.bas @@ -0,0 +1,308 @@ + rem + rem Test of IntyBASIC music player + rem by Oscar Toledo G. http://nanochess.org + rem Aug/26/2014 + rem Mar/12/2026. For FM synthesis in MSX/MSX2 and Sega Master System. + rem + + REM Include useful predefined constants + + OPTION FM ON + + BANK ROM 128 + +main: + V=0 + +wait_key: + CLS + PRINT AT 66,"Press button" + FOR c = 0 TO 60 + WAIT + NEXT c +wait_loop: + IF CONT1.BUTTON THEN GOTO play_1 + IF CONT1.BUTTON2 THEN GOTO play_2 + + GOTO wait_loop + +play_1: + PRINT AT 66,"Bach Invention 8" + PRINT AT 130,"(fragment)" + WAIT + PLAY SIMPLE + ' Harpsichord (see manual) + PLAY INSTRUMENT $B0,$B0,$B0,$B0 + PLAY tune_1 + + ' Sound effect if button touch +repeat: + WAIT + IF CONT1.BUTTON2 THEN V=15 +REM SOUND 2,300,V + IF V>0 THEN V=V-1 + IF MUSIC.PLAYING THEN GOTO repeat + GOTO wait_key + +play_2: + PRINT AT 66,"Mecha-8 Level 4 " + PRINT AT 130,"(fragment)" + WAIT + PLAY FULL + PLAY INSTRUMENT $30,$70,$a0,$30 + PLAY tune_2 +repeat2: + WAIT + IF CONT1.BUTTON THEN PLAY OFF: GOTO wait_key + GOTO repeat2 + + ' Bach Invention 8 (BWV779) + ' Fragment +tune_1: DATA BYTE 7 + MUSIC F4,- + MUSIC S,- + MUSIC A4,- + MUSIC S,- + MUSIC F4,- + MUSIC S,- + MUSIC C5,- + MUSIC S,- + MUSIC F4,- + MUSIC S,- + + MUSIC F5,- + MUSIC S,- + MUSIC E5,F3 + MUSIC D5,S + MUSIC C5,A3 + MUSIC D5,S + MUSIC C5,F3 + MUSIC A4#,S + MUSIC A4,C4 + MUSIC A4#,S + MUSIC A4,F3 + MUSIC G4,S + + MUSIC F4,F4 + MUSIC S,S + MUSIC A4,E4 + MUSIC S,D4 + MUSIC C5,C4 + MUSIC S,D4 + MUSIC A4,C4 + MUSIC S,A3# + MUSIC F5,A3 + MUSIC S,A3# + MUSIC C5,A3 + MUSIC S,G3 + + MUSIC A5,F3 + MUSIC C6,S + MUSIC A5#,A3 + MUSIC C6,S + MUSIC A5,C4 + MUSIC C6,S + MUSIC A5#,A3 + MUSIC C6,S + MUSIC A5,F4 + MUSIC C6,S + MUSIC A5#,C4 + MUSIC C6,S + + MUSIC F5,A3 + MUSIC A5,C4 + MUSIC G5,A3# + MUSIC A5,C4 + MUSIC F5,A3 + MUSIC A5,C4 + MUSIC G5,A3# + MUSIC A5,C4 + MUSIC F5,A3 + MUSIC A5,C4 + MUSIC G5,A3# + MUSIC A5,C4 + + MUSIC D5,F3 + MUSIC F5,A3 + MUSIC E5,G3 + MUSIC F5,A3 + MUSIC D5,F3 + MUSIC F5,A3 + MUSIC E5,G3 + MUSIC F5,A3 + MUSIC D5,F3 + MUSIC F5,A3 + MUSIC E5,G3 + MUSIC F5,A3 + + MUSIC B4,D3 + MUSIC S,F3 + MUSIC G4,E3 + MUSIC S,F3 + MUSIC D5,D3 + MUSIC S,F3 + MUSIC B4,E3 + MUSIC S,F3 + MUSIC F5,D3 + MUSIC S,F3 + MUSIC D5,E3 + MUSIC S,F3 + + MUSIC G5,B3 + MUSIC A5,S + MUSIC G5,G3 + MUSIC F5,S + MUSIC E5,C4 + MUSIC F5,S + MUSIC E5,G3 + MUSIC D5,S + MUSIC C5,E4 + MUSIC D5,S + MUSIC C5,C4 + MUSIC A4#,S + + MUSIC A4,F4 + MUSIC S,G4 + MUSIC D5,F4 + MUSIC C5,E4 + MUSIC B4,D4 + MUSIC C5,E4 + MUSIC B4,D4 + MUSIC A4,C4 + MUSIC G4,B3 + MUSIC A4,C4 + MUSIC G4,B3 + MUSIC F4,A3 + + MUSIC E4,G3 + MUSIC F4,S + MUSIC E4,C4 + MUSIC D4,B3 + MUSIC C4,A3 + MUSIC S,B3 + MUSIC C5,A3 + MUSIC B4,G3 + MUSIC C5,F3 + MUSIC S,G3 + MUSIC E4,F3 + MUSIC S,E3 + + MUSIC F4,D3 + MUSIC S,E3 + MUSIC C5,D3 + MUSIC S,C3 + MUSIC E4,G3 + MUSIC S,F3 + MUSIC C5,E3 + MUSIC S,F3 + MUSIC D4,G3 + MUSIC S,S + MUSIC B4,G2 + MUSIC S,S + + MUSIC C5,C4 + MUSIC S,S + MUSIC S,S + MUSIC S,S + MUSIC STOP + + ' Mecha-8 level 5: alone + ' Fragment +tune_2: DATA BYTE 5 + MUSIC G5#Y,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC F5#,G3#,-,M2 + MUSIC S,S,-,M2 + MUSIC E5,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC D5#,G3#,-,M2 + MUSIC S,S,-,M2 + MUSIC E5,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC F5#,G3#,-,M2 + MUSIC S,S,-,M2 + MUSIC G5#,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC S,G3#,-,M2 + MUSIC S,S,-,M2 + MUSIC S,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC C5#,G3#,-,M2 + MUSIC -,S,-,M2 + MUSIC G5#,C3#,-,M1 + MUSIC S,S,-,M2 + MUSIC E5,G3#,-,M2 + MUSIC -,S,-,M2 + MUSIC F5#,B2,-,M1 + MUSIC S,S,-,M2 + MUSIC S,F3#,-,M2 + MUSIC S,S,-,M2 + MUSIC S,B2,-,M1 + MUSIC S,S,-,M2 + MUSIC -,F3#,-,M2 + MUSIC -,S,-,M2 + MUSIC -,B2,-,M1 + MUSIC -,S,-,M2 + MUSIC -,F3#,-,M2 + MUSIC -,S,-,M2 + MUSIC -,B2,-,M1 + MUSIC -,S,-,M2 + MUSIC -,F3#,-,M1 + MUSIC -,S,-,M2 + MUSIC C5#,B2,-,M1 + MUSIC S,S,-,M2 + MUSIC F5#,F3#,-,M1 + MUSIC S,S,-,M2 + MUSIC C5#,B2,-,M1 + MUSIC S,S,-,M2 + MUSIC E5,A2,-,M1 + MUSIC S,S,-,M2 + MUSIC S,E3,-,M1 + MUSIC S,S,-,M2 + MUSIC S,A2,-,M1 + MUSIC S,S,-,M2 + MUSIC S,E3,-,M1 + MUSIC S,S,-,M2 + MUSIC S,A2,-,M1 + MUSIC S,S,-,M2 + MUSIC S,E3,-,M1 + MUSIC S,S,-,M2 + MUSIC S,A2,-,M1 + MUSIC S,S,-,M2 + MUSIC F5#,E3,-,M1 + MUSIC S,S,-,M2 + MUSIC E5,A2,-,M2 + MUSIC S,S,-,M2 + MUSIC D5#,E3,-,M1 + MUSIC S,S,-,M2 + MUSIC S,A2,-,M2 + MUSIC S,S,-,M2 + MUSIC C5,G2#,-,M1 + MUSIC S,S,-,M2 + MUSIC S,D3#,-,M2 + MUSIC S,S,-,M2 + MUSIC S,G2#,-,M1 + MUSIC S,S,-,M2 + MUSIC S,D3#,-,M2 + MUSIC S,S,-,M2 + MUSIC S,G2#,-,M1 + MUSIC S,S,-,M2 + MUSIC -,D3#,-,M2 + MUSIC -,S,-,M2 + MUSIC -,G2#,-,M1 + MUSIC -,S,-,M2 + MUSIC -,D3#,-,M2 + MUSIC -,S,-,M2 + MUSIC -,G2#,-,M1 + MUSIC -,S,-,M2 + MUSIC -,D3#,-,M1 + MUSIC -,S,-,M3 + MUSIC -,G2#,-,M1 + MUSIC -,S,-,M2 + MUSIC -,D3#,-,M1 + MUSIC -,S,-,M3 + MUSIC -,G2#,-,M1 + MUSIC -,S,-,M1 + MUSIC -,D3#,-,M1 + MUSIC -,S,-,M1 + MUSIC REPEAT diff --git a/examples/oscar_compressed_sms.bas b/examples/oscar_compressed_sms.bas new file mode 100644 index 0000000..6cc7adb --- /dev/null +++ b/examples/oscar_compressed_sms.bas @@ -0,0 +1,437 @@ + ' TMSColor 3.0 Apr/27/2025 + ' Command: ../tmscolor/tmscolor -sms -z -p2 -b -t64 oscar_sms.bmp ../cvbasic/examples/oscar_compressed_sms.bas + ' Created: Sun Apr 27 11:43:16 2025 + + ' Sega Master System lacks VRAM to show a full screen picture. + + ' Display image. + MODE 4 + DEFINE CHAR PLETTER 64,185,image_char + PALETTE 9,$0B + PALETTE 11,$1B + PALETTE 14,$2B + SCREEN image_pattern,0,200,16,12,16 + WHILE 1: WEND + + ' Start tile = 64. Total_tiles = 185 +image_char: + DATA BYTE $69,$b4,$ff,$00,$24,$8f,$03,$00 + DATA BYTE $e4,$03,$7f,$00,$00,$41,$1f,$00 + DATA BYTE $01,$ff,$fe,$ff,$dd,$03,$1f,$ba + DATA BYTE $61,$03,$00,$80,$80,$00,$02,$c0 + DATA BYTE $c0,$00,$c0,$40,$f0,$b0,$17,$f0 + DATA BYTE $00,$f0,$00,$01,$03,$04,$f0,$f4 + DATA BYTE $f4,$00,$f4,$33,$00,$79,$23,$de + DATA BYTE $03,$27,$80,$f9,$00,$80,$6f,$07 + DATA BYTE $22,$03,$f7,$00,$80,$f3,$00,$26 + DATA BYTE $80,$fb,$00,$34,$69,$0f,$03,$81 + DATA BYTE $8f,$a2,$03,$bf,$00,$f9,$13,$04 + DATA BYTE $8f,$03,$2a,$c7,$03,$ab,$e3,$03 + DATA BYTE $2f,$71,$03,$4f,$ae,$6a,$03,$f3 + DATA BYTE $00,$fe,$ec,$03,$00,$6e,$fc,$11 + DATA BYTE $03,$f8,$fd,$00,$00,$3f,$71,$00 + DATA BYTE $03,$04,$3b,$9c,$07,$b3,$29,$7b + DATA BYTE $00,$00,$de,$17,$01,$03,$0f,$b3 + DATA BYTE $b3,$bf,$07,$3f,$33,$1c,$03,$1b + DATA BYTE $64,$03,$7d,$03,$ff,$10,$fd,$ff + DATA BYTE $c0,$2e,$f9,$a0,$70,$30,$70,$f0 + DATA BYTE $a3,$60,$03,$ee,$ee,$e8,$00,$0e + DATA BYTE $e2,$e2,$e0,$80,$03,$44,$ec,$00 + DATA BYTE $80,$cc,$00,$00,$84,$96,$f8,$00 + DATA BYTE $5b,$f1,$00,$a0,$f3,$e6,$7b,$a2 + DATA BYTE $0b,$0f,$00,$20,$00,$07,$00,$80 + DATA BYTE $0f,$8f,$0f,$e0,$03,$a0,$1f,$bf + DATA BYTE $3f,$20,$b0,$0f,$03,$a0,$0f,$af + DATA BYTE $2f,$ed,$03,$51,$3b,$cf,$11,$00 + DATA BYTE $00,$c7,$00,$10,$c1,$18,$00,$04 + DATA BYTE $c0,$b5,$48,$f5,$00,$00,$fc,$bb + DATA BYTE $00,$07,$bb,$6f,$07,$8b,$b7,$bb + DATA BYTE $0b,$03,$e2,$a7,$61,$69,$03,$63 + DATA BYTE $e7,$30,$00,$61,$97,$98,$60,$17 + DATA BYTE $44,$40,$e8,$a7,$06,$41,$ec,$b8 + DATA BYTE $f1,$b3,$1a,$e8,$97,$c4,$bf,$cc + DATA BYTE $44,$83,$63,$66,$99,$07,$02,$46 + DATA BYTE $1d,$bf,$24,$c2,$cb,$77,$30,$03 + DATA BYTE $17,$bb,$c5,$cb,$e1,$f3,$bc,$74 + DATA BYTE $a3,$27,$aa,$85,$bb,$cf,$07,$ae + DATA BYTE $ce,$03,$a3,$27,$6f,$84,$87,$ef + DATA BYTE $f1,$0b,$68,$8f,$03,$48,$35,$07 + DATA BYTE $e6,$00,$7f,$e7,$03,$17,$a3,$31 + DATA BYTE $03,$f4,$0b,$e0,$8f,$03,$e4,$39 + DATA BYTE $03,$0f,$f2,$c2,$8f,$eb,$98,$00 + DATA BYTE $00,$e7,$64,$00,$ef,$00,$00,$5a + DATA BYTE $ed,$00,$2f,$03,$af,$00,$84,$af + DATA BYTE $20,$f0,$03,$d0,$f0,$20,$e1,$c1 + DATA BYTE $e0,$bb,$03,$0b,$8b,$03,$00,$e8 + DATA BYTE $00,$12,$f4,$f5,$4c,$bf,$12,$40 + DATA BYTE $e3,$00,$60,$00,$97,$b5,$80,$84 + DATA BYTE $e3,$fd,$02,$00,$7d,$7d,$7b,$07 + DATA BYTE $fb,$f9,$7f,$07,$1e,$ff,$fa,$ef + DATA BYTE $82,$fb,$df,$b7,$00,$18,$0f,$fe + DATA BYTE $9f,$6f,$f9,$8b,$a7,$88,$b7,$80 + DATA BYTE $2f,$88,$07,$30,$0f,$c7,$f8,$cf + DATA BYTE $83,$00,$03,$23,$23,$95,$db,$02 + DATA BYTE $c5,$a3,$bd,$03,$84,$b7,$20,$00 + DATA BYTE $c1,$e1,$e1,$60,$80,$e0,$e0,$40 + DATA BYTE $04,$08,$48,$48,$00,$78,$00,$00 + DATA BYTE $44,$79,$00,$80,$79,$b0,$7a,$2d + DATA BYTE $0b,$7d,$00,$11,$87,$7e,$69,$00 + DATA BYTE $0b,$c6,$ab,$ee,$98,$00,$00,$a7 + DATA BYTE $04,$41,$a7,$a7,$00,$27,$97,$b3 + DATA BYTE $b7,$17,$01,$af,$a8,$b7,$27,$ff + DATA BYTE $f9,$f7,$10,$87,$fb,$ff,$0b,$46 + DATA BYTE $0c,$bf,$0c,$c0,$8f,$22,$06,$8f + DATA BYTE $00,$00,$87,$00,$01,$c0,$fb,$bb + DATA BYTE $d3,$f0,$fe,$0e,$07,$e3,$83,$cb + DATA BYTE $c4,$a3,$64,$c7,$03,$40,$9d,$03 + DATA BYTE $17,$c9,$f3,$c2,$af,$f0,$c7,$0b + DATA BYTE $a8,$85,$b7,$fd,$03,$f3,$8f,$3c + DATA BYTE $78,$03,$26,$7a,$fd,$03,$b8,$1b + DATA BYTE $07,$bc,$ff,$30,$07,$b8,$93,$99 + DATA BYTE $98,$03,$bc,$ab,$4c,$3e,$03,$63 + DATA BYTE $1e,$03,$5f,$1e,$03,$1c,$7f,$03 + DATA BYTE $17,$d7,$ce,$00,$3f,$03,$ff,$9b + DATA BYTE $af,$c2,$f3,$63,$c0,$03,$88,$1c + DATA BYTE $03,$08,$e7,$5e,$03,$c3,$12,$e0 + DATA BYTE $cb,$c7,$cf,$f4,$d7,$5e,$03,$5a + DATA BYTE $fa,$00,$72,$03,$97,$a2,$81,$00 + DATA BYTE $00,$89,$00,$2c,$00,$ad,$00,$23 + DATA BYTE $07,$ce,$01,$de,$de,$df,$d3,$93 + DATA BYTE $d8,$03,$e3,$46,$ff,$03,$70,$07 + DATA BYTE $03,$83,$7f,$7c,$22,$ff,$c2,$f7 + DATA BYTE $00,$fb,$c0,$f1,$b1,$f0,$c0,$f8 + DATA BYTE $b8,$3a,$f8,$c0,$8f,$78,$9f,$e5 + DATA BYTE $ee,$00,$c4,$00,$d8,$d8,$18,$00 + DATA BYTE $f8,$f8,$64,$20,$9b,$b0,$40,$63 + DATA BYTE $e0,$0c,$00,$3f,$31,$2f,$0c,$6f + DATA BYTE $61,$6f,$00,$4f,$01,$00,$00,$93 + DATA BYTE $00,$40,$90,$b7,$00,$fb,$00,$8b + DATA BYTE $e3,$b8,$03,$fb,$96,$30,$00,$88 + DATA BYTE $4f,$03,$00,$01,$03,$11,$02,$03 + DATA BYTE $05,$03,$07,$31,$80,$d7,$21,$dc + DATA BYTE $83,$48,$8f,$5e,$1f,$8f,$03,$f8 + DATA BYTE $07,$a3,$7e,$df,$a6,$3c,$31,$03 + DATA BYTE $0e,$03,$03,$ae,$20,$1b,$07,$cf + DATA BYTE $d0,$e3,$98,$0a,$03,$02,$c6,$03 + DATA BYTE $82,$f1,$a3,$f3,$8b,$c9,$86,$16 + DATA BYTE $e7,$0b,$cf,$9c,$37,$0b,$e6,$03 + DATA BYTE $1f,$1b,$03,$9f,$7f,$37,$07,$0f + DATA BYTE $1d,$03,$07,$73,$f8,$e7,$4f,$3f + DATA BYTE $e3,$8f,$fc,$eb,$2e,$cb,$06,$f0 + DATA BYTE $e3,$0f,$fc,$7a,$a7,$77,$e9,$c7 + DATA BYTE $e6,$cb,$4a,$e1,$00,$01,$a2,$1c + DATA BYTE $f5,$b5,$21,$01,$67,$66,$67,$b2 + DATA BYTE $ff,$dd,$00,$ff,$80,$10,$6f,$6f + DATA BYTE $7f,$18,$c7,$f9,$56,$80,$51,$03 + DATA BYTE $00,$2f,$18,$fe,$e6,$fc,$08,$fe + DATA BYTE $f6,$09,$fe,$08,$e6,$e6,$d3,$57 + DATA BYTE $ce,$00,$16,$e3,$84,$e7,$b8,$0b + DATA BYTE $b8,$38,$00,$18,$00,$f8,$03,$0e + DATA BYTE $2f,$54,$21,$83,$2f,$70,$2f,$03 + DATA BYTE $27,$00,$37,$8b,$00,$00,$b7,$00 + DATA BYTE $45,$07,$27,$00,$a5,$bb,$6f,$66 + DATA BYTE $00,$c7,$82,$e0,$f3,$01,$f3,$f1 + DATA BYTE $a9,$43,$f7,$97,$01,$a1,$c0,$81 + DATA BYTE $f1,$aa,$12,$ef,$e0,$00,$60,$60 + DATA BYTE $71,$f0,$f8,$e0,$00,$f8,$70,$fe + DATA BYTE $ee,$fe,$03,$07,$04,$80,$ac,$0f + DATA BYTE $08,$0e,$07,$05,$0f,$0a,$4d,$00 + DATA BYTE $0f,$07,$1f,$18,$0f,$05,$1f,$1a + DATA BYTE $0e,$0f,$0d,$17,$12,$03,$7f,$1f + DATA BYTE $17,$ef,$7d,$53,$03,$13,$ab,$bf + DATA BYTE $d6,$9b,$1d,$c2,$e3,$5e,$0f,$6d + DATA BYTE $89,$03,$05,$9a,$74,$1b,$43,$01 + DATA BYTE $8e,$00,$47,$3b,$3b,$fb,$f0,$0d + DATA BYTE $0d,$f9,$ca,$03,$e7,$40,$04,$04 + DATA BYTE $fc,$f0,$0c,$0c,$70,$f0,$03,$20 + DATA BYTE $dc,$dc,$80,$a8,$1c,$1c,$7c,$e0 + DATA BYTE $e7,$4f,$bc,$18,$07,$1a,$87,$f0 + DATA BYTE $c3,$ca,$00,$10,$0b,$07,$f0,$e3 + DATA BYTE $9b,$01,$90,$6f,$f9,$63,$ab,$22 + DATA BYTE $77,$03,$98,$67,$03,$35,$10,$ef + DATA BYTE $89,$e7,$3f,$ae,$73,$07,$03,$f4 + DATA BYTE $1c,$4b,$f4,$eb,$5d,$0b,$b8,$03 + DATA BYTE $f0,$e4,$d7,$cf,$1b,$9b,$d3,$e2 + DATA BYTE $ef,$72,$02,$ab,$3c,$3f,$ab,$ee + DATA BYTE $03,$ee,$0b,$83,$75,$bc,$00,$14 + DATA BYTE $c3,$00,$00,$5c,$a0,$00,$e3,$3a + DATA BYTE $4a,$eb,$46,$00,$e2,$e3,$c9,$bf + DATA BYTE $16,$e6,$00,$66,$00,$88,$03,$2e + DATA BYTE $00,$00,$3e,$b8,$00,$bb,$98,$01 + DATA BYTE $9f,$65,$00,$08,$00,$a1,$eb,$7c + DATA BYTE $00,$60,$b8,$00,$cf,$c3,$c7,$ef + DATA BYTE $e8,$ef,$9c,$f9,$0c,$1f,$45,$13 + DATA BYTE $95,$38,$26,$0f,$2f,$04,$0f,$00 + DATA BYTE $0b,$0f,$24,$7e,$5a,$2e,$e4,$fe + DATA BYTE $38,$1a,$e6,$03,$e0,$f6,$18,$16 + DATA BYTE $e6,$f4,$c5,$00,$b7,$fc,$7f,$fc + DATA BYTE $97,$8c,$03,$fe,$82,$d9,$06,$df + DATA BYTE $3e,$0f,$cf,$a3,$2c,$f1,$a6,$10 + DATA BYTE $13,$e3,$11,$11,$03,$05,$87,$16 + DATA BYTE $e4,$11,$f2,$81,$f0,$19,$64,$64 + DATA BYTE $18,$03,$0d,$d7,$03,$8c,$77,$02 + DATA BYTE $76,$00,$12,$2f,$2d,$1f,$b1,$0e + DATA BYTE $0e,$3f,$40,$a7,$0b,$3f,$b6,$19 + DATA BYTE $09,$3e,$10,$9f,$70,$60,$d0,$00 + DATA BYTE $60,$40,$7f,$3c,$63,$43,$7c,$04 + DATA BYTE $70,$ce,$8e,$70,$40,$da,$1c,$87 + DATA BYTE $dd,$ab,$98,$df,$70,$06,$cc,$33 + DATA BYTE $33,$68,$fc,$0f,$c5,$1c,$c5,$00 + DATA BYTE $28,$af,$1d,$8b,$c0,$db,$db,$ff + DATA BYTE $3d,$42,$17,$42,$3f,$1d,$ff,$c1 + DATA BYTE $a9,$04,$fa,$bd,$60,$e5,$e5,$1e + DATA BYTE $32,$cd,$3b,$cd,$3e,$03,$94,$1f + DATA BYTE $c0,$51,$cb,$38,$19,$80,$82,$da + DATA BYTE $37,$ba,$1f,$33,$f2,$0a,$ca,$ca + DATA BYTE $1f,$03,$9e,$5c,$07,$e7,$40,$60 + DATA BYTE $98,$98,$e0,$01,$82,$9f,$b2,$52 + DATA BYTE $38,$38,$03,$c0,$93,$43,$02,$92 + DATA BYTE $60,$03,$0f,$82,$7c,$7c,$30,$80 + DATA BYTE $20,$e3,$58,$04,$03,$0c,$cf,$03 + DATA BYTE $87,$f3,$e3,$df,$eb,$c3,$e2,$e3 + DATA BYTE $f8,$f7,$89,$04,$f3,$84,$8b,$10 + DATA BYTE $e3,$df,$ed,$5b,$6e,$13,$e3,$26 + DATA BYTE $3b,$3c,$03,$1b,$97,$76,$1f,$6f + DATA BYTE $3f,$3c,$9f,$c7,$39,$cc,$3d,$f7 + DATA BYTE $fb,$d8,$03,$13,$80,$ce,$c7,$67 + DATA BYTE $80,$5f,$f2,$cf,$71,$6f,$10,$0f + DATA BYTE $85,$b7,$c5,$f7,$d8,$f7,$4f,$99 + DATA BYTE $00,$0e,$97,$c5,$02,$cf,$00,$9d + DATA BYTE $3b,$9f,$dc,$43,$fd,$ec,$07,$9f + DATA BYTE $52,$f7,$01,$26,$8b,$b8,$0c,$cd + DATA BYTE $1d,$ff,$01,$e2,$ff,$0d,$b3,$b2 + DATA BYTE $af,$10,$18,$8b,$f0,$e0,$c7,$00 + DATA BYTE $07,$e3,$80,$e1,$61,$a1,$c0,$e3 + DATA BYTE $38,$23,$e1,$a9,$bb,$59,$af,$8d + DATA BYTE $04,$9a,$e0,$10,$c1,$d2,$54,$86 + DATA BYTE $82,$07,$fb,$5f,$15,$f3,$f7,$8c + DATA BYTE $03,$e7,$db,$d2,$07,$90,$6a,$26 + DATA BYTE $97,$78,$07,$ff,$0e,$b7,$8b,$d7 + DATA BYTE $c2,$cf,$c0,$c0,$33,$c0,$dc,$00 + DATA BYTE $dc,$63,$c7,$43,$9f,$3b,$03,$07 + DATA BYTE $a9,$4f,$07,$a1,$43,$2f,$ff,$d0 + DATA BYTE $3f,$88,$e3,$08,$14,$01,$14,$08 + DATA BYTE $0c,$f1,$f1,$ac,$1c,$1b,$d6,$fd + DATA BYTE $4a,$03,$d2,$d8,$fd,$a7,$57,$b0 + DATA BYTE $a0,$e0,$7b,$48,$f7,$b7,$f8,$06 + DATA BYTE $71,$ff,$8e,$ff,$60,$23,$3d,$20 + DATA BYTE $bd,$bf,$3d,$24,$ff,$00,$04,$b1 + DATA BYTE $00,$a7,$81,$e1,$e4,$1b,$1b,$e7 + DATA BYTE $f6,$17,$68,$0f,$83,$1c,$03,$30 + DATA BYTE $81,$f6,$3c,$84,$e7,$be,$4c,$f2 + DATA BYTE $bf,$fe,$01,$e2,$1d,$1d,$fe,$c2 + DATA BYTE $3d,$3d,$a2,$03,$82,$d2,$b2,$fe + DATA BYTE $83,$bf,$2a,$2f,$92,$28,$cb,$82 + DATA BYTE $d0,$d0,$10,$50,$96,$50,$50,$64 + DATA BYTE $9b,$9b,$11,$64,$7c,$83,$15,$3f + DATA BYTE $c0,$1f,$91,$d9,$03,$a3,$b0,$f0 + DATA BYTE $49,$8f,$50,$03,$3f,$03,$9c,$9c + DATA BYTE $00,$e1,$0f,$fc,$02,$02,$22,$05 + DATA BYTE $03,$5d,$f8,$07,$e5,$0f,$34,$e3 + DATA BYTE $3f,$20,$02,$b3,$b3,$33,$20,$bd + DATA BYTE $bd,$e3,$51,$a7,$a7,$27,$c0,$0f + DATA BYTE $00,$a3,$a3,$22,$00,$25,$a1,$a1 + DATA BYTE $eb,$5c,$f9,$00,$e7,$45,$00,$9f + DATA BYTE $00,$a8,$bb,$13,$df,$be,$f2,$ce + DATA BYTE $57,$03,$36,$af,$f7,$a3,$22,$84 + DATA BYTE $00,$00,$44,$00,$d1,$9f,$bb,$03 + DATA BYTE $96,$13,$f7,$00,$a2,$1f,$41,$00 + DATA BYTE $30,$00,$10,$85,$80,$f6,$f6,$e0 + DATA BYTE $68,$7e,$76,$29,$78,$08,$1a,$13 + DATA BYTE $af,$40,$00,$43,$bd,$bf,$02,$d7 + DATA BYTE $d5,$10,$d7,$00,$83,$00,$40,$0a + DATA BYTE $6a,$0b,$0a,$40,$02,$42,$aa,$50 + DATA BYTE $40,$00,$ca,$ff,$75,$b8,$d4,$ff + DATA BYTE $71,$c5,$f3,$5d,$30,$bb,$2d,$85 + DATA BYTE $f3,$e1,$eb,$e3,$fe,$0c,$10,$3d + DATA BYTE $0c,$43,$b6,$9d,$00,$f3,$ee,$28 + DATA BYTE $e7,$eb,$b8,$d7,$95,$e0,$a1,$b3 + DATA BYTE $e1,$bb,$95,$20,$5b,$00,$ca,$fb + DATA BYTE $e3,$c9,$03,$10,$08,$00,$82,$3c + DATA BYTE $3c,$f8,$f1,$db,$4c,$80,$3e,$2f + DATA BYTE $e7,$03,$9e,$04,$b8,$8b,$a1,$30 + DATA BYTE $78,$7c,$04,$74,$93,$9b,$d0,$0b + DATA BYTE $68,$68,$40,$c6,$3b,$70,$8f,$9b + DATA BYTE $6d,$49,$03,$54,$0a,$13,$17,$82 + DATA BYTE $b8,$e7,$41,$21,$27,$06,$27,$dd + DATA BYTE $32,$bb,$cc,$0c,$03,$76,$de,$03 + DATA BYTE $30,$0e,$1e,$07,$17,$88,$4b,$88 + DATA BYTE $e7,$1e,$63,$1f,$43,$12,$af,$03 + DATA BYTE $e3,$10,$9f,$13,$2c,$2c,$92,$94 + DATA BYTE $1e,$30,$0f,$03,$9e,$77,$89,$ef + DATA BYTE $bd,$36,$c3,$62,$08,$8b,$4e,$36 + DATA BYTE $fe,$fb,$14,$f1,$7b,$f1,$5e,$27 + DATA BYTE $f7,$ff,$0c,$7e,$81,$81,$ff,$8f + DATA BYTE $28,$9d,$c0,$20,$20,$df,$26,$d9 + DATA BYTE $d9,$07,$3e,$44,$bb,$bb,$7c,$51 + DATA BYTE $4b,$0d,$af,$f1,$e3,$77,$0b,$07 + DATA BYTE $07,$c6,$39,$39,$fe,$10,$df,$38 + DATA BYTE $c6,$c6,$2f,$38,$80,$ce,$22,$80 + DATA BYTE $40,$a3,$71,$c0,$e3,$02,$1b,$e4 + DATA BYTE $e4,$1f,$5f,$ff,$26,$5f,$53,$fb + DATA BYTE $0f,$90,$03,$07,$db,$57,$e6,$00 + DATA BYTE $bf,$1f,$0a,$f5,$3b,$f5,$0a,$11 + DATA BYTE $c6,$03,$f1,$3b,$a3,$e1,$b0,$03 + DATA BYTE $99,$dc,$c1,$03,$02,$04,$fd,$fd + DATA BYTE $03,$04,$c1,$e8,$e0,$0c,$91,$9d + DATA BYTE $9d,$7c,$44,$81,$0c,$2d,$90,$80 + DATA BYTE $64,$3c,$81,$03,$3e,$61,$81,$80 + DATA BYTE $a2,$80,$03,$1d,$80,$17,$d2,$bf + DATA BYTE $6b,$63,$00,$a7,$f3,$00,$03,$30 + DATA BYTE $e1,$f1,$e1,$18,$e1,$00,$fb,$e1 + DATA BYTE $09,$d0,$df,$d0,$04,$18,$48,$1e + DATA BYTE $fc,$e2,$92,$00,$02,$f3,$13,$e2 + DATA BYTE $83,$21,$c3,$09,$bb,$80,$c3,$e7 + DATA BYTE $c8,$03,$0c,$0f,$80,$04,$18,$31 + DATA BYTE $31,$11,$cf,$e2,$79,$00,$77,$00 + DATA BYTE $d2,$03,$c2,$30,$1f,$40,$8f,$8f + DATA BYTE $87,$9a,$06,$03,$02,$0d,$0f,$8c + DATA BYTE $03,$01,$5a,$63,$01,$2f,$18,$33 + DATA BYTE $2f,$67,$a3,$bf,$63,$b7,$f3,$03 + DATA BYTE $8b,$e3,$e2,$03,$40,$24,$00,$21 + DATA BYTE $00,$7c,$00,$00,$74,$74,$12,$ab + DATA BYTE $34,$00,$e6,$c3,$65,$e0,$b7,$23 + DATA BYTE $e2,$47,$0d,$60,$b4,$dd,$5f,$5f + DATA BYTE $09,$4f,$00,$17,$17,$db,$96,$47 + DATA BYTE $00,$31,$cf,$86,$08,$0a,$d3,$02 + DATA BYTE $eb,$73,$7e,$80,$b2,$03,$db,$ee + DATA BYTE $40,$03,$5e,$70,$3a,$a2,$00,$01 + DATA BYTE $01,$e2,$e4,$e2,$0a,$0e,$70,$00 + DATA BYTE $12,$2f,$70,$50,$2c,$11,$00,$0d + DATA BYTE $18,$bf,$a7,$bf,$2c,$fb,$fc,$ec + DATA BYTE $68,$fc,$97,$16,$51,$87,$cf,$dc + DATA BYTE $07,$e3,$48,$03,$07,$83,$01,$00 + DATA BYTE $c2,$c2,$c3,$01,$62,$62,$63,$01 + DATA BYTE $96,$db,$12,$89,$db,$06,$96,$cf + DATA BYTE $0e,$c0,$03,$02,$f9,$06,$06,$72 + DATA BYTE $f9,$eb,$f1,$f7,$53,$cc,$91,$dd + DATA BYTE $d5,$9d,$77,$61,$03,$13,$f7,$7f + DATA BYTE $47,$83,$a1,$0d,$ee,$82,$bb,$8b + DATA BYTE $a0,$c7,$c7,$e0,$13,$ad,$c3,$f3 + DATA BYTE $dd,$c3,$cf,$83,$d8,$07,$ff,$c0 + DATA BYTE $f0,$3c,$c3,$c3,$fc,$c8,$97,$64 + DATA BYTE $78,$27,$72,$7e,$83,$cb,$88,$cf + DATA BYTE $d8,$27,$3b,$27,$f8,$4b,$15,$bf + DATA BYTE $08,$dd,$17,$03,$84,$cf,$02,$0c + DATA BYTE $06,$0b,$09,$06,$f7,$22,$0e,$88 + DATA BYTE $ee,$10,$8c,$8c,$73,$73,$63,$8c + DATA BYTE $17,$03,$4a,$03,$b6,$b6,$03,$1a + DATA BYTE $fb,$fb,$0b,$46,$03,$6b,$07,$18 + DATA BYTE $70,$03,$cf,$31,$1e,$03,$17,$8f + DATA BYTE $03,$05,$08,$03,$24,$c8,$00,$04 + DATA BYTE $da,$89,$00,$04,$db,$00,$c0,$84 + DATA BYTE $e3,$80,$00,$39,$3d,$39,$84,$3a + DATA BYTE $3e,$3a,$08,$c1,$ef,$e6,$03,$10 + DATA BYTE $f5,$b3,$04,$71,$71,$01,$08,$03 + DATA BYTE $09,$53,$00,$af,$08,$8c,$ba,$ba + DATA BYTE $aa,$af,$5b,$00,$4a,$d3,$40,$2f + DATA BYTE $00,$02,$21,$23,$23,$02,$43,$01 + DATA BYTE $84,$61,$7c,$90,$20,$c1,$cc,$e1 + DATA BYTE $bb,$22,$38,$fc,$00,$1d,$fc,$b0 + DATA BYTE $0e,$0b,$de,$03,$7c,$40,$8b,$58 + DATA BYTE $87,$05,$f9,$7c,$23,$91,$bb,$42 + DATA BYTE $bc,$4e,$00,$b0,$ac,$82,$80,$2e + DATA BYTE $01,$90,$00,$9c,$2d,$08,$a0,$26 + DATA BYTE $26,$b1,$22,$83,$d8,$38,$0a,$72 + DATA BYTE $05,$07,$05,$06,$e0,$06,$00,$ce + DATA BYTE $16,$bf,$b2,$dc,$03,$63,$7f,$03 + DATA BYTE $3f,$7c,$e6,$27,$c0,$3f,$b7,$8c + DATA BYTE $27,$0f,$f2,$67,$07,$46,$c7,$31 + DATA BYTE $03,$5b,$03,$1b,$8f,$03,$1f,$3b + DATA BYTE $1b,$03,$03,$b2,$3e,$70,$96,$39 + DATA BYTE $00,$20,$f6,$44,$47,$9b,$71,$03 + DATA BYTE $c3,$73,$83,$61,$a3,$32,$3f,$f7 + DATA BYTE $8a,$1f,$0e,$87,$c9,$0f,$06,$a7 + DATA BYTE $80,$07,$03,$0c,$0c,$03,$41,$41 + DATA BYTE $02,$f5,$c5,$c3,$85,$97,$e9,$f7 + DATA BYTE $5f,$57,$fb,$76,$b7,$68,$63,$03 + DATA BYTE $67,$bb,$a3,$4b,$f3,$ef,$2f,$b2 + DATA BYTE $03,$c3,$d8,$ff,$c7,$4d,$88,$3f + DATA BYTE $8f,$ec,$29,$c3,$47,$01,$60,$03 + DATA BYTE $9a,$f7,$d3,$2b,$38,$77,$d6,$a5 + DATA BYTE $f7,$09,$03,$7d,$83,$82,$03,$03 + DATA BYTE $1b,$fb,$fd,$ce,$03,$97,$50,$e1 + DATA BYTE $1f,$1e,$1f,$e2,$c3,$60,$00,$03 + DATA BYTE $80,$db,$5b,$85,$03,$fb,$7b,$fb + DATA BYTE $bb,$b7,$77,$d9,$03,$4f,$ba,$3c + DATA BYTE $03,$01,$03,$6b,$1f,$8c,$1b,$40 + DATA BYTE $03,$ee,$cf,$78,$f0,$03,$1c,$ce + DATA BYTE $cb,$23,$3f,$fe,$07,$7e,$ff,$67 + DATA BYTE $03,$7f,$99,$03,$12,$46,$d3,$22 + DATA BYTE $00,$b8,$c3,$5d,$7c,$03,$07,$30 + DATA BYTE $03,$bc,$03,$3c,$7e,$98,$00,$3d + DATA BYTE $03,$3e,$c6,$00,$1e,$68,$03,$ca + DATA BYTE $32,$af,$d3,$ee,$15,$cb,$03,$11 + DATA BYTE $07,$04,$c2,$cb,$00,$07,$c1,$2f + DATA BYTE $2e,$2f,$c2,$23,$03,$21,$22,$c3 + DATA BYTE $23,$20,$23,$89,$8b,$07,$bd,$03 + DATA BYTE $46,$e7,$b4,$0b,$b2,$7c,$a7,$5b + DATA BYTE $a3,$03,$7f,$ef,$17,$f1,$03,$01 + DATA BYTE $e3,$07,$fe,$c6,$03,$fc,$26,$03 + DATA BYTE $ee,$fd,$cb,$a7,$8e,$bd,$e9,$58 + DATA BYTE $a6,$78,$e4,$b9,$e8,$bd,$67,$b3 + DATA BYTE $7a,$01,$31,$0e,$04,$01,$02,$96 + DATA BYTE $01,$15,$ca,$17,$60,$f3,$01,$7f + DATA BYTE $12,$ed,$ed,$1e,$1c,$d4,$8a,$5f + DATA BYTE $d5,$3a,$57,$01,$94,$e3,$e0,$c1 + DATA BYTE $d7,$d8,$df,$a4,$c2,$f6,$20,$aa + DATA BYTE $e3,$4d,$2c,$f0,$f1,$16,$f2,$7e + DATA BYTE $ef,$0c,$d3,$bd,$44,$ab,$52,$05 + DATA BYTE $67,$0b,$20,$83,$56,$61,$13,$e3 + DATA BYTE $01,$14,$03,$80,$5f,$ae,$78,$e2 + DATA BYTE $ac,$f1,$03,$66,$d0,$97,$31,$f0 + DATA BYTE $23,$e1,$81,$83,$c5,$e7,$12,$b3 + DATA BYTE $bf,$eb,$3f,$4d,$03,$89,$c7,$60 + DATA BYTE $e1,$e3,$74,$03,$89,$9b,$b1,$00 + DATA BYTE $fc,$fe,$8f,$00,$ff,$59,$00,$1e + DATA BYTE $84,$cb,$1e,$c7,$07,$3c,$9d,$03 + DATA BYTE $0b,$bc,$9f,$e0,$41,$33,$32,$23 + DATA BYTE $00,$4c,$30,$00,$01,$03,$48,$05 + DATA BYTE $02,$20,$b3,$2e,$00,$bf,$05,$06 + DATA BYTE $83,$fc,$03,$10,$63,$02,$e7,$31 + DATA BYTE $73,$0f,$d2,$8f,$03,$d0,$18,$03 + DATA BYTE $d3,$03,$d8,$c6,$03,$49,$6f,$2b + DATA BYTE $13,$cf,$1f,$ab,$97,$c6,$03,$bf + DATA BYTE $b8,$cb,$4d,$3f,$70,$f7,$99,$fc + DATA BYTE $05,$cc,$03,$e0,$03,$68,$60,$11 + DATA BYTE $be,$c6,$03,$d7,$31,$03,$d6,$03 + DATA BYTE $db,$c5,$85,$f9,$03,$f4,$81,$7d + DATA BYTE $0f,$ac,$00,$10,$f1,$07,$02,$e1 + DATA BYTE $83,$7c,$f9,$4c,$20,$01,$a6,$1b + DATA BYTE $01,$18,$2e,$01,$0c,$01,$ff,$2d + DATA BYTE $6f,$34,$9b,$e1,$a8,$03,$ee,$f4 + DATA BYTE $4f,$71,$20,$c7,$00,$62,$67,$02 + DATA BYTE $04,$c3,$c7,$03,$f4,$03,$21,$83 + DATA BYTE $87,$5e,$87,$8f,$07,$15,$e0,$ca + DATA BYTE $dd,$a7,$1d,$b1,$8c,$30,$9b,$91 + DATA BYTE $70,$80,$c0,$4d,$b7,$88,$d6,$af + DATA BYTE $e7,$87,$45,$e0,$46,$00,$9b,$f8 + DATA BYTE $11,$f0,$a1,$7f,$e0,$14,$01,$3f + DATA BYTE $82,$5d,$f0,$31,$00,$01,$03,$8b + DATA BYTE $8c,$03,$8f,$03,$79,$af,$03,$bc + DATA BYTE $ab,$56,$2a,$ac,$00,$fe,$f9,$ab + DATA BYTE $b1,$03,$f8,$e3,$03,$fc,$ef,$03 + DATA BYTE $f1,$9f,$f9,$03,$06,$94,$93,$02 + DATA BYTE $ce,$03,$07,$22,$00,$98,$00,$00 + DATA BYTE $f9,$00,$d2,$df,$30,$03,$90,$ff + DATA BYTE $b1,$ee,$4d,$84,$c3,$0f,$c2,$fb + DATA BYTE $e6,$03,$0c,$34,$03,$06,$b7,$0b + DATA BYTE $00,$8b,$07,$00,$f1,$00,$5e,$b7 + DATA BYTE $66,$0d,$31,$03,$29,$03,$55,$ae + DATA BYTE $13,$e3,$fe,$c3,$ab,$63,$f6,$03 + DATA BYTE $88,$3f,$03,$ff,$ff,$ff,$f0 + + ' Width = 16, height = 12 +image_pattern: + DATA BYTE $40,$00,$41,$00,$42,$00,$43,$00,$44,$00,$45,$00,$46,$00,$47,$00,$48,$00,$48,$00,$48,$00,$49,$00,$4a,$00,$4b,$00,$4c,$00,$4d,$00 + DATA BYTE $4e,$00,$4f,$00,$50,$00,$51,$00,$52,$00,$53,$00,$54,$00,$55,$00,$56,$00,$48,$00,$48,$00,$57,$00,$58,$00,$59,$00,$5a,$00,$5b,$00 + DATA BYTE $5c,$00,$5d,$00,$5e,$00,$5f,$00,$60,$00,$61,$00,$62,$00,$63,$00,$64,$00,$65,$00,$66,$00,$67,$00,$68,$00,$69,$00,$6a,$00,$6b,$00 + DATA BYTE $6c,$00,$6d,$00,$6e,$00,$6f,$00,$70,$00,$71,$00,$72,$00,$73,$00,$74,$00,$75,$00,$76,$00,$77,$00,$78,$00,$79,$00,$7a,$00,$7b,$00 + DATA BYTE $7c,$00,$7d,$00,$7e,$00,$7f,$00,$80,$00,$81,$00,$82,$00,$83,$00,$84,$00,$85,$00,$86,$00,$87,$00,$88,$00,$89,$00,$8a,$00,$8b,$00 + DATA BYTE $8c,$00,$8d,$00,$8e,$00,$8f,$00,$90,$00,$91,$00,$92,$00,$93,$00,$94,$00,$95,$00,$96,$00,$97,$00,$98,$00,$99,$00,$9a,$00,$9b,$00 + DATA BYTE $9c,$00,$9d,$00,$9e,$00,$9f,$00,$a0,$00,$a1,$00,$a2,$00,$a3,$00,$a4,$00,$a5,$00,$a6,$00,$a7,$00,$a8,$00,$a9,$00,$aa,$00,$ab,$00 + DATA BYTE $ac,$00,$ad,$00,$ae,$00,$af,$00,$b0,$00,$b1,$00,$b2,$00,$b3,$00,$b4,$00,$b5,$00,$b6,$00,$b7,$00,$b8,$00,$b9,$00,$ba,$00,$bb,$00 + DATA BYTE $bc,$00,$bd,$00,$be,$00,$bf,$00,$c0,$00,$c1,$00,$c2,$00,$c3,$00,$c4,$00,$c5,$00,$c6,$00,$c7,$00,$c8,$00,$c9,$00,$ca,$00,$cb,$00 + DATA BYTE $6d,$00,$cc,$00,$cd,$00,$ce,$00,$cf,$00,$d0,$00,$d1,$00,$d2,$00,$d3,$00,$d4,$00,$d5,$00,$d6,$00,$d7,$00,$d8,$00,$d9,$00,$da,$00 + DATA BYTE $db,$00,$dc,$00,$dd,$00,$de,$00,$df,$00,$e0,$00,$e1,$00,$e2,$00,$e3,$00,$48,$00,$e4,$00,$e5,$00,$e6,$00,$e7,$00,$e8,$00,$e9,$00 + DATA BYTE $ea,$00,$eb,$00,$ec,$00,$ed,$00,$ee,$00,$ef,$00,$f0,$00,$f1,$00,$f2,$00,$48,$00,$f3,$00,$f4,$00,$f5,$00,$f6,$00,$f7,$00,$f8,$00 diff --git a/examples/oscar_nes.bas b/examples/oscar_nes.bas new file mode 100644 index 0000000..2149b4c --- /dev/null +++ b/examples/oscar_nes.bas @@ -0,0 +1,1100 @@ + ' TMSColor 3.2 Aug/26/2025 + ' Command: ./tmscolor -nes -p1 -t128 -b oscar.png oscar.bas + ' Created: Tue Aug 26 10:07:21 2025 + + ' Display image. + PALETTE 0,$0F + PALETTE 1,$16 + PALETTE 2,$27 + PALETTE 3,$38 + SCREEN DISABLE + WAIT ' Make sure the screen is disabled + ' so a big update can happen. + SCREEN image_pattern,0,$00ea,12,16,12 + WAIT ' Make sure all the data is processed. + SCREEN ENABLE + WHILE 1: WEND + + ' Start tile = 128. Total_tiles = 118 + CHRROM 0 PATTERN 128 + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "12222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222233" + BITMAP "22223333" + BITMAP "22333333" + + BITMAP "22222223" + BITMAP "22222333" + BITMAP "23223333" + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222223" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22223333" + BITMAP "22233333" + BITMAP "22333333" + + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22223333" + + BITMAP "22333333" + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "22223333" + BITMAP "22233333" + BITMAP "22233333" + BITMAP "22333333" + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333322" + BITMAP "33332222" + BITMAP "33322211" + BITMAP "33222111" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33323333" + BITMAP "22222222" + BITMAP "11111111" + BITMAP "11100111" + BITMAP "11111111" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "22333333" + BITMAP "11112333" + BITMAP "11111223" + + BITMAP "22222222" + BITMAP "22222223" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22223333" + BITMAP "22223333" + BITMAP "22233333" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333332" + BITMAP "33333332" + BITMAP "33333322" + BITMAP "33333322" + + BITMAP "33221111" + BITMAP "32211111" + BITMAP "22211111" + BITMAP "22111111" + BITMAP "21111111" + BITMAP "21111112" + BITMAP "11112222" + BITMAP "11122222" + + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "22222221" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "11111112" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "22111111" + BITMAP "22221111" + + BITMAP "22333333" + BITMAP "11122333" + BITMAP "11000233" + BITMAP "11100023" + BITMAP "11100001" + BITMAP "11100000" + BITMAP "11100000" + BITMAP "11110000" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "13333333" + BITMAP "01333333" + BITMAP "00233333" + + BITMAP "22233333" + BITMAP "22333333" + BITMAP "22333333" + BITMAP "22333333" + BITMAP "23333333" + BITMAP "23333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "33333321" + BITMAP "33333321" + BITMAP "33333321" + BITMAP "33333212" + BITMAP "33333212" + BITMAP "33333222" + BITMAP "33333222" + BITMAP "33333222" + + BITMAP "11222222" + BITMAP "12222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "22221111" + BITMAP "22222111" + BITMAP "22222111" + BITMAP "22222211" + BITMAP "22222221" + BITMAP "22222221" + BITMAP "22222221" + BITMAP "22222221" + + BITMAP "11110000" + BITMAP "11110000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111100" + + BITMAP "00033333" + BITMAP "00023333" + BITMAP "00003333" + BITMAP "00002333" + BITMAP "00002333" + BITMAP "00001333" + BITMAP "00000333" + BITMAP "00000233" + + BITMAP "22222222" + BITMAP "22222223" + BITMAP "22222223" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22222333" + + BITMAP "33332122" + BITMAP "33332122" + BITMAP "33332122" + BITMAP "33332222" + BITMAP "33333122" + BITMAP "33333122" + BITMAP "33332111" + BITMAP "33332111" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222232" + BITMAP "22232233" + BITMAP "22222222" + BITMAP "11122222" + BITMAP "11112222" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22233333" + BITMAP "22233222" + BITMAP "22222111" + BITMAP "22221110" + BITMAP "22221112" + + BITMAP "22222221" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "32222111" + BITMAP "22221111" + BITMAP "01111111" + BITMAP "11222211" + BITMAP "33333222" + + BITMAP "11111100" + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11110000" + BITMAP "11110001" + + BITMAP "00000233" + BITMAP "00000232" + BITMAP "00000132" + BITMAP "00000122" + BITMAP "01110122" + BITMAP "11000112" + BITMAP "00001111" + BITMAP "00001122" + + BITMAP "33333333" + BITMAP "22233333" + BITMAP "22222233" + BITMAP "22222223" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "23333333" + BITMAP "23333333" + + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22223333" + + BITMAP "33332111" + BITMAP "33222122" + BITMAP "32321222" + BITMAP "32101211" + BITMAP "10233110" + BITMAP "12333111" + BITMAP "33333112" + BITMAP "33233122" + + BITMAP "11111122" + BITMAP "22222112" + BITMAP "22212111" + BITMAP "11110111" + BITMAP "22000111" + BITMAP "22111101" + BITMAP "22222211" + BITMAP "22222212" + + BITMAP "22211222" + BITMAP "22211221" + BITMAP "22111211" + BITMAP "22111121" + BITMAP "22111122" + BITMAP "22111122" + BITMAP "22211122" + BITMAP "22211122" + + BITMAP "33332112" + BITMAP "12231001" + BITMAP "23110110" + BITMAP "22211221" + BITMAP "21222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "20011111" + BITMAP "11111111" + BITMAP "11222111" + BITMAP "11222111" + BITMAP "21222211" + BITMAP "21222211" + BITMAP "21222211" + BITMAP "12222111" + + BITMAP "10000222" + BITMAP "10001221" + BITMAP "10000221" + BITMAP "10001121" + BITMAP "11001121" + BITMAP "11111121" + BITMAP "11111121" + BITMAP "11101111" + + BITMAP "23333333" + BITMAP "23333333" + BITMAP "22333333" + BITMAP "22333333" + BITMAP "22333333" + BITMAP "22233333" + BITMAP "22233333" + BITMAP "22223333" + + BITMAP "22223333" + BITMAP "22223333" + BITMAP "22222333" + BITMAP "22223333" + BITMAP "22223333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22223333" + + BITMAP "33232122" + BITMAP "33322122" + BITMAP "33332122" + BITMAP "33333211" + BITMAP "33333222" + BITMAP "33333322" + BITMAP "33333322" + BITMAP "33333322" + + BITMAP "22222112" + BITMAP "22222122" + BITMAP "22221122" + BITMAP "12122222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "22221112" + BITMAP "22211111" + BITMAP "22211112" + BITMAP "22211111" + BITMAP "22211111" + BITMAP "22221211" + BITMAP "32222221" + BITMAP "22111111" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222232" + BITMAP "22223332" + BITMAP "12222332" + BITMAP "12222222" + BITMAP "12222222" + BITMAP "22222222" + + BITMAP "12222111" + BITMAP "22222111" + BITMAP "22222111" + BITMAP "22222111" + BITMAP "22222111" + BITMAP "22222111" + BITMAP "22221111" + BITMAP "22221111" + + BITMAP "11101211" + BITMAP "11112222" + BITMAP "11111112" + BITMAP "11111222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + + BITMAP "22223333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222223" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "22333333" + + BITMAP "22223333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22222333" + + BITMAP "33333332" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP "22222222" + BITMAP "22222212" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "32222222" + BITMAP "32222111" + BITMAP "33222211" + BITMAP "33222222" + + BITMAP "22112222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222211" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11222222" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22111222" + BITMAP "11112222" + BITMAP "11222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "22221211" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222211" + BITMAP "22222111" + + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11112222" + BITMAP "11101222" + + BITMAP "22233333" + BITMAP "22223333" + BITMAP "22222233" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "22333333" + BITMAP "22223333" + BITMAP "22222333" + BITMAP "22222233" + + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222333" + BITMAP "22222233" + + BITMAP "33322222" + BITMAP "33322222" + BITMAP "33332222" + BITMAP "33332122" + BITMAP "33333222" + BITMAP "33333212" + BITMAP "33333322" + BITMAP "33333332" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222112" + BITMAP "22221112" + BITMAP "22211111" + BITMAP "22211111" + + BITMAP "22222111" + BITMAP "22221111" + BITMAP "22211111" + BITMAP "22111111" + BITMAP "21111111" + BITMAP "11111112" + BITMAP "11211132" + BITMAP "11112321" + + BITMAP "11001222" + BITMAP "11001222" + BITMAP "10101222" + BITMAP "11102212" + BITMAP "21112112" + BITMAP "11022022" + BITMAP "01132022" + BITMAP "10231132" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "12222222" + BITMAP "00122222" + + BITMAP "22222333" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222333" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222233" + BITMAP "22222233" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333321" + BITMAP "33333100" + BITMAP "33321010" + + BITMAP "33333332" + BITMAP "33333211" + BITMAP "33321002" + BITMAP "32100112" + BITMAP "20011122" + BITMAP "00111123" + BITMAP "10110232" + BITMAP "01111322" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22111111" + BITMAP "22111111" + BITMAP "22122222" + BITMAP "22112222" + BITMAP "21012222" + BITMAP "21012222" + + BITMAP "22111111" + BITMAP "21111111" + BITMAP "11111222" + BITMAP "12222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222223" + BITMAP "22222233" + + BITMAP "11223311" + BITMAP "22223201" + BITMAP "22232110" + BITMAP "22331102" + BITMAP "23321113" + BITMAP "33211023" + BITMAP "33110233" + BITMAP "32011332" + + BITMAP "02320231" + BITMAP "13311320" + BITMAP "23202310" + BITMAP "33013201" + BITMAP "31033101" + BITMAP "20232010" + BITMAP "11331010" + BITMAP "02320110" + + BITMAP "00012222" + BITMAP "00012222" + BITMAP "00000112" + BITMAP "00000001" + BITMAP "00000000" + BITMAP "11000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "12222222" + BITMAP "01222222" + BITMAP "00122222" + BITMAP "00012222" + BITMAP "00000112" + + BITMAP "22222223" + BITMAP "22222223" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333331" + BITMAP "33333210" + BITMAP "23333111" + BITMAP "22232111" + BITMAP "22331110" + + BITMAP "33200100" + BITMAP "32011001" + BITMAP "10110011" + BITMAP "01110011" + BITMAP "11100111" + BITMAP "11101110" + BITMAP "01011111" + BITMAP "11111101" + + BITMAP "11102322" + BITMAP "11112322" + BITMAP "01123322" + BITMAP "11123222" + BITMAP "11132222" + BITMAP "21022222" + BITMAP "21122222" + BITMAP "20022122" + + BITMAP "21012222" + BITMAP "21012222" + BITMAP "21011222" + BITMAP "21011222" + BITMAP "21011222" + BITMAP "11011222" + BITMAP "22011222" + BITMAP "22101232" + + BITMAP "22222333" + BITMAP "22223333" + BITMAP "22233332" + BITMAP "22333331" + BITMAP "23333311" + BITMAP "22223211" + BITMAP "22222110" + BITMAP "22221112" + + BITMAP "21102320" + BITMAP "11023310" + BITMAP "01133202" + BITMAP "10232013" + BITMAP "02331023" + BITMAP "13320233" + BITMAP "23301331" + BITMAP "33102320" + + BITMAP "13201001" + BITMAP "33101000" + BITMAP "32011000" + BITMAP "31010000" + BITMAP "20100010" + BITMAP "01100110" + BITMAP "01000000" + BITMAP "11001100" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "00000001" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "22222222" + BITMAP "01222222" + BITMAP "00122222" + BITMAP "00012222" + BITMAP "00001222" + BITMAP "00000122" + BITMAP "00000112" + BITMAP "00000000" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "11222222" + + BITMAP "22321110" + BITMAP "22321100" + BITMAP "22321100" + BITMAP "22311100" + BITMAP "23311101" + BITMAP "23310101" + BITMAP "23310111" + BITMAP "23320111" + + BITMAP "10111102" + BITMAP "01111112" + BITMAP "11111112" + BITMAP "11111112" + BITMAP "11111022" + BITMAP "11111121" + BITMAP "11111121" + BITMAP "11111111" + + BITMAP "20023112" + BITMAP "10013212" + BITMAP "10001222" + BITMAP "00001222" + BITMAP "00001222" + BITMAP "00001222" + BITMAP "00012223" + BITMAP "00012233" + + BITMAP "22201322" + BITMAP "22201322" + BITMAP "22222223" + BITMAP "22222333" + BITMAP "22222332" + BITMAP "32222322" + BITMAP "33212221" + BITMAP "33202101" + + BITMAP "22221113" + BITMAP "22211023" + BITMAP "22111233" + BITMAP "21111332" + BITMAP "11103331" + BITMAP "11023310" + BITMAP "10233201" + BITMAP "02332013" + + BITMAP "32013310" + BITMAP "31133201" + BITMAP "20233011" + BITMAP "01331011" + BITMAP "03320111" + BITMAP "23311110" + BITMAP "33211000" + BITMAP "32111111" + + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11110000" + BITMAP "11000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000001" + BITMAP "00000001" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "11000000" + BITMAP "11000000" + BITMAP "11100000" + BITMAP "11100000" + + BITMAP "00012222" + BITMAP "00000122" + BITMAP "00000002" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + + BITMAP "23310111" + BITMAP "23301111" + BITMAP "23201111" + BITMAP "23111111" + BITMAP "22111111" + BITMAP "22011111" + BITMAP "21111111" + BITMAP "21111111" + + BITMAP "11111111" + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11101110" + BITMAP "11101110" + BITMAP "11011100" + BITMAP "11011100" + BITMAP "10011100" + + BITMAP "00012333" + BITMAP "00123333" + BITMAP "00123333" + BITMAP "01223333" + BITMAP "01223333" + BITMAP "01233333" + BITMAP "12233333" + BITMAP "12333332" + + BITMAP "33201110" + BITMAP "33201102" + BITMAP "33111123" + BITMAP "32111122" + BITMAP "21111100" + BITMAP "21111011" + BITMAP "11123201" + BITMAP "11133311" + + BITMAP "23331122" + BITMAP "33201110" + BITMAP "32111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "10111111" + BITMAP "11111111" + BITMAP "11101111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "10000001" + BITMAP "11100001" + BITMAP "11111001" + BITMAP "11111001" + BITMAP "11111001" + BITMAP "11111011" + BITMAP "11110011" + BITMAP "11100011" + + BITMAP "11110000" + BITMAP "11110000" + BITMAP "11110000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111000" + BITMAP "11111100" + BITMAP "11111100" + + BITMAP "00000000" + BITMAP "00000001" + BITMAP "00000012" + BITMAP "00000222" + BITMAP "00002322" + BITMAP "00013332" + BITMAP "00023333" + BITMAP "00133333" + + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222222" + BITMAP "22222221" + BITMAP "22222221" + BITMAP "22222210" + BITMAP "22222211" + + BITMAP "21111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111110" + BITMAP "11111110" + + BITMAP "10111001" + BITMAP "11111001" + BITMAP "11111012" + BITMAP "11110012" + BITMAP "11110122" + BITMAP "11110122" + BITMAP "11101222" + BITMAP "11001123" + + BITMAP "23333222" + BITMAP "23332232" + BITMAP "23223331" + BITMAP "32233321" + BITMAP "22333321" + BITMAP "23333321" + BITMAP "33332211" + BITMAP "33332211" + + BITMAP "11133311" + BITMAP "11112101" + BITMAP "11110111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11110111" + BITMAP "11100111" + BITMAP "11101111" + BITMAP "11001111" + BITMAP "10011111" + + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "11100011" + BITMAP "11000111" + BITMAP "11000111" + BITMAP "11000111" + BITMAP "11000111" + BITMAP "11001111" + BITMAP "10001111" + BITMAP "10001111" + + BITMAP "11111110" + BITMAP "11111110" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "10000000" + BITMAP "10000000" + BITMAP "10000000" + BITMAP "11000000" + + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00000000" + BITMAP "00100000" + BITMAP "00100001" + BITMAP "00101102" + + BITMAP "00233333" + BITMAP "01333323" + BITMAP "02333323" + BITMAP "13333223" + BITMAP "23333223" + BITMAP "23333223" + BITMAP "33332233" + BITMAP "33332233" + + + ' Width = 12, height = 16 +image_pattern: + DATA BYTE $80,$81,$81,$81,$82,$83,$84,$84,$84,$84,$84,$84 + DATA BYTE $81,$81,$81,$85,$86,$84,$84,$84,$84,$84,$84,$84 + DATA BYTE $81,$81,$87,$88,$84,$84,$84,$84,$84,$84,$84,$84 + DATA BYTE $81,$81,$89,$8a,$8b,$8c,$84,$84,$84,$84,$84,$84 + DATA BYTE $81,$8d,$8e,$8f,$90,$91,$92,$93,$84,$84,$84,$84 + DATA BYTE $81,$94,$95,$96,$81,$97,$98,$99,$84,$84,$84,$84 + DATA BYTE $9a,$84,$9b,$9c,$9d,$9e,$9f,$a0,$a1,$a2,$84,$84 + DATA BYTE $a3,$84,$a4,$a5,$a6,$a7,$a8,$a9,$81,$aa,$84,$84 + DATA BYTE $ab,$84,$ac,$ad,$ae,$af,$b0,$b1,$81,$b2,$b3,$84 + DATA BYTE $b4,$84,$b5,$b6,$b7,$b8,$b9,$ba,$81,$81,$bb,$bc + DATA BYTE $bd,$84,$84,$be,$81,$bf,$c0,$c1,$c2,$81,$81,$81 + DATA BYTE $c3,$84,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$81,$81 + DATA BYTE $cc,$cd,$ce,$cf,$d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7 + DATA BYTE $81,$d8,$d9,$da,$db,$dc,$dd,$de,$df,$d4,$d4,$e0 + DATA BYTE $81,$e1,$e2,$e3,$e4,$e5,$e6,$e7,$e8,$d4,$d4,$e9 + DATA BYTE $ea,$eb,$ec,$ed,$ee,$ef,$f0,$f1,$f2,$f3,$f4,$f5 diff --git a/examples/oscar_sms.bas b/examples/oscar_sms.bas new file mode 100644 index 0000000..213c3bf --- /dev/null +++ b/examples/oscar_sms.bas @@ -0,0 +1,772 @@ + ' TMSColor 3.0 Apr/27/2025 + ' Command: ../tmscolor/tmscolor -sms -p2 -b -t64 oscar_sms.bmp ../cvbasic/examples/oscar_sms.bas + ' Created: Sun Apr 27 11:43:16 2025 + + ' Sega Master System lacks VRAM to show a full screen picture. + + ' Display image. + MODE 4 + DEFINE CHAR 64,185,image_char + PALETTE 9,$0B + PALETTE 11,$1B + PALETTE 14,$2B + SCREEN image_pattern,0,200,16,12,16 + WHILE 1: WEND + + ' Start tile = 64. Total_tiles = 185 +image_char: + DATA BYTE $b4,$ff,$ff,$ff,$24,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$7f,$7f,$7f,$00,$1f,$1f,$1f + DATA BYTE $01,$ff,$fe,$ff,$01,$ff,$fe,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$00,$00,$00,$80,$80,$00,$80 + DATA BYTE $c0,$c0,$00,$c0,$40,$f0,$b0,$f0 + DATA BYTE $00,$f0,$f0,$f0,$00,$f0,$f0,$f0 + DATA BYTE $04,$f0,$f4,$f4,$00,$f4,$f4,$f4 + DATA BYTE $79,$ff,$ff,$ff,$79,$ff,$ff,$ff + DATA BYTE $79,$ff,$ff,$ff,$79,$ff,$ff,$ff + DATA BYTE $79,$ff,$ff,$ff,$79,$ff,$ff,$ff + DATA BYTE $79,$ff,$ff,$ff,$79,$ff,$ff,$ff + DATA BYTE $80,$f9,$f9,$f9,$80,$ff,$ff,$ff + DATA BYTE $80,$ff,$ff,$ff,$80,$ff,$ff,$ff + DATA BYTE $80,$ff,$ff,$ff,$80,$f7,$f7,$f7 + DATA BYTE $80,$f3,$f3,$f3,$80,$fb,$fb,$fb + DATA BYTE $34,$ff,$ff,$ff,$34,$ff,$ff,$ff + DATA BYTE $34,$7f,$7f,$7f,$34,$7f,$7f,$7f + DATA BYTE $34,$bf,$bf,$bf,$34,$ff,$ff,$ff + DATA BYTE $34,$ff,$ff,$ff,$04,$ff,$ff,$ff + DATA BYTE $2a,$ff,$ff,$ff,$2a,$ff,$ff,$ff + DATA BYTE $2a,$ff,$ff,$ff,$2a,$ff,$ff,$ff + DATA BYTE $ab,$ff,$ff,$ff,$ab,$ff,$ff,$ff + DATA BYTE $ab,$ff,$ff,$ff,$ab,$ff,$ff,$ff + DATA BYTE $2f,$ff,$ff,$ff,$2f,$ff,$ff,$ff + DATA BYTE $2f,$ff,$ff,$ff,$4f,$ff,$ff,$ff + DATA BYTE $4f,$ff,$ff,$ff,$4f,$ff,$ff,$ff + DATA BYTE $4f,$ff,$ff,$ff,$4f,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$fe,$ff,$ff,$ff + DATA BYTE $fe,$ff,$ff,$ff,$fe,$fe,$fe,$fe + DATA BYTE $fc,$fe,$fe,$fe,$fc,$fe,$fe,$fe + DATA BYTE $fc,$fe,$fe,$fe,$f8,$fd,$fd,$fd + DATA BYTE $00,$3f,$3f,$3f,$00,$3f,$3f,$3f + DATA BYTE $04,$3b,$3f,$3f,$00,$7f,$7f,$7f + DATA BYTE $00,$7b,$7b,$7b,$00,$fd,$fd,$fd + DATA BYTE $00,$fd,$fd,$fd,$00,$fd,$fd,$fd + DATA BYTE $0f,$b3,$b3,$bf,$07,$3f,$3b,$3f + DATA BYTE $03,$7f,$7f,$7f,$03,$7f,$7f,$7f + DATA BYTE $03,$7f,$7d,$7f,$03,$ff,$fd,$ff + DATA BYTE $c0,$3f,$3f,$f9,$a0,$70,$70,$f0 + DATA BYTE $00,$f4,$f4,$f0,$00,$ee,$ee,$e8 + DATA BYTE $00,$e2,$e2,$e0,$80,$e2,$e2,$e0 + DATA BYTE $80,$ec,$ec,$ec,$80,$cc,$cc,$cc + DATA BYTE $00,$f9,$f9,$f8,$00,$f1,$f1,$f1 + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$fe,$fe,$fe + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$0f,$0f,$0f,$00,$07,$07,$07 + DATA BYTE $80,$0f,$8f,$0f,$80,$0f,$8f,$0f + DATA BYTE $a0,$1f,$bf,$3f,$b0,$0f,$bf,$3f + DATA BYTE $a0,$0f,$af,$2f,$a0,$0f,$af,$2f + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$cf,$cf,$cf,$00,$c7,$c7,$c7 + DATA BYTE $10,$c1,$c1,$c1,$04,$c0,$c0,$c0 + DATA BYTE $00,$f5,$f5,$f5,$00,$fc,$fc,$fc + DATA BYTE $00,$f5,$f5,$f5,$00,$f1,$f1,$f1 + DATA BYTE $00,$f5,$f5,$f5,$00,$f4,$f4,$f4 + DATA BYTE $00,$f1,$f1,$f1,$00,$f1,$f1,$f1 + DATA BYTE $79,$ff,$ff,$ff,$79,$ff,$ff,$ff + DATA BYTE $69,$ff,$ff,$ff,$63,$e7,$e7,$e7 + DATA BYTE $61,$f3,$f3,$f3,$60,$f1,$f1,$f1 + DATA BYTE $44,$e8,$ec,$ec,$06,$ec,$ee,$ee + DATA BYTE $80,$ff,$ff,$ff,$80,$ff,$ff,$ff + DATA BYTE $e8,$97,$ff,$ff,$80,$ff,$ff,$ff + DATA BYTE $80,$ff,$ff,$ff,$cc,$83,$cf,$cf + DATA BYTE $66,$99,$ff,$ff,$02,$1d,$1f,$1f + DATA BYTE $24,$f7,$f7,$f7,$30,$f7,$f7,$f7 + DATA BYTE $30,$7f,$7f,$7f,$34,$ff,$ff,$ff + DATA BYTE $34,$ff,$ff,$ff,$34,$ff,$ff,$ff + DATA BYTE $24,$ff,$ff,$ff,$74,$ff,$ff,$ff + DATA BYTE $ab,$ff,$ff,$ff,$aa,$ff,$ff,$ff + DATA BYTE $2a,$ff,$ff,$ff,$aa,$ff,$ff,$ff + DATA BYTE $ae,$ff,$ff,$ff,$ae,$ff,$ff,$ff + DATA BYTE $ae,$ff,$ff,$ff,$ae,$ff,$ff,$ff + DATA BYTE $4f,$ff,$ff,$ff,$6f,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ef,$ff,$ff,$ff + DATA BYTE $6f,$ff,$ff,$ff,$68,$ff,$ff,$ff + DATA BYTE $48,$ff,$ff,$ff,$68,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$7f,$ff,$ff,$ff + DATA BYTE $7f,$ff,$ff,$ff,$7f,$ff,$ff,$ff + DATA BYTE $f8,$fd,$fd,$fd,$f8,$fd,$fd,$fd + DATA BYTE $f4,$ff,$ff,$ff,$e0,$ff,$ff,$ff + DATA BYTE $e4,$ff,$ff,$ff,$e4,$ff,$ff,$ff + DATA BYTE $f4,$ff,$ff,$ff,$f2,$ff,$ff,$ff + DATA BYTE $00,$eb,$eb,$eb,$00,$fb,$fb,$fb + DATA BYTE $00,$ef,$ef,$ef,$00,$ed,$ed,$ed + DATA BYTE $00,$ed,$ed,$ed,$00,$af,$af,$af + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $20,$f0,$d0,$f0,$20,$e1,$c1,$e0 + DATA BYTE $20,$e1,$c1,$e0,$20,$f0,$d0,$f0 + DATA BYTE $20,$f0,$d0,$f0,$00,$e8,$e8,$e8 + DATA BYTE $00,$80,$80,$80,$00,$c0,$c0,$c0 + DATA BYTE $40,$e3,$e3,$e3,$00,$e7,$e7,$e7 + DATA BYTE $80,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$fd,$02,$7d,$7d,$7b + DATA BYTE $07,$fb,$f9,$7f,$07,$ff,$fa,$ef + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$df,$df,$df + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$fe,$00,$fd,$fd,$f9 + DATA BYTE $a0,$0f,$af,$2f,$a0,$0f,$af,$2f + DATA BYTE $a0,$1f,$bf,$3f,$80,$2f,$af,$2f + DATA BYTE $30,$0f,$3f,$3f,$00,$07,$07,$07 + DATA BYTE $00,$03,$23,$23,$00,$00,$00,$00 + DATA BYTE $02,$c0,$c0,$c0,$00,$c0,$c0,$c0 + DATA BYTE $00,$c0,$c0,$c0,$00,$c0,$c0,$c0 + DATA BYTE $00,$c1,$c1,$c1,$20,$c1,$e1,$e1 + DATA BYTE $60,$80,$e0,$e0,$40,$08,$48,$48 + DATA BYTE $00,$78,$78,$78,$00,$79,$79,$79 + DATA BYTE $80,$79,$f9,$f9,$00,$78,$78,$78 + DATA BYTE $00,$7d,$7d,$7d,$00,$7f,$7f,$7f + DATA BYTE $00,$7e,$7e,$7e,$00,$7d,$7d,$7d + DATA BYTE $00,$fe,$fe,$fe,$00,$ee,$ee,$ee + DATA BYTE $00,$e3,$e3,$e3,$41,$a7,$e7,$e7 + DATA BYTE $27,$97,$b3,$b7,$17,$af,$a8,$b7 + DATA BYTE $27,$ff,$f9,$f7,$34,$ff,$fb,$ff + DATA BYTE $0b,$0c,$0f,$0f,$0c,$ff,$ff,$ff + DATA BYTE $06,$8f,$8f,$8f,$00,$87,$87,$87 + DATA BYTE $c0,$fb,$bb,$d3,$f0,$fe,$0e,$f0 + DATA BYTE $00,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $74,$ff,$ff,$ff,$64,$ff,$ff,$ff + DATA BYTE $40,$ff,$ff,$ff,$40,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$3f,$3f,$3f + DATA BYTE $00,$ef,$ef,$ef,$f0,$ff,$ff,$ff + DATA BYTE $a8,$ff,$ff,$ff,$aa,$ff,$ff,$ff + DATA BYTE $aa,$ff,$ff,$ff,$aa,$ff,$ff,$ff + DATA BYTE $aa,$ff,$ff,$ff,$aa,$ff,$ff,$ff + DATA BYTE $2a,$ff,$ff,$ff,$2a,$ff,$ff,$ff + DATA BYTE $78,$ff,$ff,$ff,$78,$ff,$ff,$ff + DATA BYTE $7a,$fd,$ff,$ff,$b8,$ff,$ff,$ff + DATA BYTE $bc,$ff,$ff,$ff,$b8,$ff,$ff,$ff + DATA BYTE $b8,$fd,$fd,$fd,$98,$fd,$fd,$fd + DATA BYTE $7f,$ff,$ff,$ff,$7f,$ff,$ff,$ff + DATA BYTE $7f,$ff,$ff,$ff,$3e,$ff,$ff,$ff + DATA BYTE $1e,$ff,$ff,$ff,$5f,$ff,$ff,$ff + DATA BYTE $1c,$ff,$ff,$ff,$1c,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$7f,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $3f,$ff,$ff,$ff,$3f,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$fe,$ff,$ff,$ff + DATA BYTE $fe,$ff,$ff,$ff,$fe,$ff,$ff,$ff + DATA BYTE $e0,$ff,$ff,$ff,$c0,$ff,$ff,$ff + DATA BYTE $88,$ff,$ff,$ff,$08,$fb,$fb,$fb + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $12,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$bf,$bf,$bf,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$fe,$fe,$fe + DATA BYTE $00,$fe,$fe,$fe,$00,$fa,$fa,$fa + DATA BYTE $00,$fa,$fa,$fa,$00,$fc,$fc,$fc + DATA BYTE $00,$81,$81,$81,$00,$89,$89,$89 + DATA BYTE $00,$ad,$ad,$ad,$00,$ff,$ff,$ce + DATA BYTE $01,$de,$de,$df,$01,$ff,$fe,$ff + DATA BYTE $01,$ff,$fe,$ff,$01,$ff,$fe,$ff + DATA BYTE $07,$ff,$fa,$ff,$03,$ff,$fe,$ff + DATA BYTE $03,$ff,$fe,$ff,$83,$7f,$7c,$ff + DATA BYTE $c2,$fb,$f9,$fb,$c0,$f1,$b1,$f0 + DATA BYTE $c0,$f8,$b8,$f8,$c0,$f0,$b0,$f0 + DATA BYTE $00,$e3,$e3,$e3,$00,$c7,$c7,$c4 + DATA BYTE $00,$d8,$d8,$18,$00,$f8,$f8,$20 + DATA BYTE $00,$f0,$f0,$40,$00,$fe,$fe,$e0 + DATA BYTE $0c,$3f,$31,$2f,$0c,$6f,$61,$6f + DATA BYTE $00,$01,$01,$01,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$40,$70,$70 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$7d,$7d,$7d,$00,$7d,$7d,$7d + DATA BYTE $00,$78,$78,$78,$00,$30,$30,$30 + DATA BYTE $00,$01,$01,$01,$00,$03,$03,$03 + DATA BYTE $01,$03,$02,$03,$05,$03,$02,$07 + DATA BYTE $31,$ff,$ff,$ff,$21,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $e0,$ff,$1f,$ff,$e0,$ff,$1f,$ff + DATA BYTE $e0,$ff,$1f,$ff,$e0,$ff,$1f,$ff + DATA BYTE $f8,$ff,$ff,$ff,$e0,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $3c,$ff,$ff,$ff,$0e,$ff,$ff,$ff + DATA BYTE $03,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$07,$f8,$f8,$ff + DATA BYTE $2a,$ff,$ff,$ff,$0a,$ff,$ff,$ff + DATA BYTE $02,$ff,$ff,$ff,$82,$ff,$ff,$ff + DATA BYTE $40,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$80,$7f,$7f,$ff + DATA BYTE $b8,$fe,$fe,$fe,$9c,$ff,$ff,$ff + DATA BYTE $9c,$ff,$ff,$ff,$9c,$ff,$ff,$ff + DATA BYTE $9c,$ff,$ff,$ff,$9c,$ff,$ff,$ff + DATA BYTE $9c,$ff,$ff,$ff,$9c,$ff,$ff,$ff + DATA BYTE $1f,$ff,$ff,$ff,$9f,$7f,$ff,$ff + DATA BYTE $1f,$ff,$ff,$ff,$0f,$ff,$ff,$ff + DATA BYTE $0f,$ff,$ff,$ff,$0f,$ff,$ff,$ff + DATA BYTE $07,$ff,$ff,$ff,$03,$ff,$ff,$ff + DATA BYTE $3f,$ff,$ff,$ff,$3f,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$3f,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$7f,$ff,$ff,$ff + DATA BYTE $fe,$ff,$ff,$ff,$fe,$ff,$ff,$ff + DATA BYTE $fe,$ff,$ff,$ff,$f8,$ff,$ff,$ff + DATA BYTE $f0,$ff,$ff,$ff,$fe,$ff,$ff,$ff + DATA BYTE $fe,$ff,$ff,$ff,$fc,$fe,$fe,$fe + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$fe,$fe,$fe + DATA BYTE $00,$e8,$e8,$e8,$00,$e1,$e1,$e1 + DATA BYTE $01,$f7,$f7,$f5,$03,$03,$01,$03 + DATA BYTE $01,$67,$66,$67,$00,$07,$07,$07 + DATA BYTE $00,$03,$03,$03,$00,$1f,$1f,$1f + DATA BYTE $10,$6f,$6f,$7f,$18,$ff,$ef,$ff + DATA BYTE $80,$ff,$fe,$ff,$80,$fe,$fe,$fe + DATA BYTE $18,$fe,$e6,$fc,$08,$fe,$f6,$fe + DATA BYTE $08,$e6,$e6,$ee,$00,$ce,$ce,$ce + DATA BYTE $c0,$f0,$b0,$f0,$c0,$f0,$b0,$f0 + DATA BYTE $00,$b8,$b8,$38,$00,$18,$18,$18 + DATA BYTE $00,$18,$18,$18,$00,$18,$18,$18 + DATA BYTE $00,$18,$18,$18,$00,$18,$18,$18 + DATA BYTE $0e,$2f,$21,$2f,$00,$2f,$2f,$2f + DATA BYTE $00,$2f,$2f,$27,$00,$37,$37,$37 + DATA BYTE $00,$b7,$b7,$b7,$00,$37,$37,$37 + DATA BYTE $00,$27,$27,$27,$00,$0f,$0f,$0f + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$80,$00,$80 + DATA BYTE $e0,$f3,$01,$f3,$f1,$ff,$08,$f7 + DATA BYTE $97,$ff,$00,$ff,$c0,$ff,$3f,$ff + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$e0,$e0,$e0,$60,$f0,$c0,$f0 + DATA BYTE $f0,$f8,$e0,$f8,$70,$fe,$ee,$fe + DATA BYTE $03,$07,$04,$07,$07,$0f,$08,$07 + DATA BYTE $05,$0f,$0a,$0f,$0f,$0f,$00,$0f + DATA BYTE $07,$1f,$18,$0f,$05,$1f,$1a,$0f + DATA BYTE $0d,$17,$12,$0f,$0d,$17,$12,$1f + DATA BYTE $e0,$ff,$1f,$ff,$e0,$ff,$1f,$ff + DATA BYTE $e0,$ff,$1f,$ff,$e0,$ff,$1f,$ff + DATA BYTE $c0,$ff,$3f,$ff,$c0,$ff,$3f,$ff + DATA BYTE $c0,$ff,$3f,$ff,$40,$ff,$bf,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $07,$f8,$f8,$ff,$0f,$f0,$f0,$ff + DATA BYTE $0f,$f0,$f0,$ff,$0f,$f0,$f0,$ff + DATA BYTE $0f,$f0,$f0,$ff,$05,$fa,$fa,$ff + DATA BYTE $00,$ff,$ff,$ff,$01,$fe,$fe,$ff + DATA BYTE $c0,$3b,$3b,$fb,$f0,$0d,$0d,$f9 + DATA BYTE $f0,$0d,$0d,$fd,$f8,$04,$04,$fc + DATA BYTE $f0,$0c,$0c,$f0,$f0,$0c,$0c,$f0 + DATA BYTE $20,$dc,$dc,$e0,$e0,$1c,$1c,$e0 + DATA BYTE $9c,$ff,$ff,$ff,$9c,$ff,$ff,$ff + DATA BYTE $bc,$ff,$ff,$ff,$9c,$ff,$ff,$ff + DATA BYTE $1a,$fd,$ff,$ff,$18,$fe,$fe,$fe + DATA BYTE $10,$ff,$ff,$ff,$18,$ff,$ff,$ff + DATA BYTE $03,$ff,$ff,$ff,$01,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $08,$f7,$ff,$ff,$88,$77,$ff,$ff + DATA BYTE $98,$67,$ff,$ff,$10,$ef,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$7f,$ff,$ff,$ff + DATA BYTE $3f,$ff,$ff,$ff,$7f,$ff,$ff,$ff + DATA BYTE $3f,$ff,$ff,$ff,$7f,$ff,$ff,$ff + DATA BYTE $3f,$ff,$ff,$ff,$3f,$ff,$ff,$ff + DATA BYTE $f4,$fe,$fe,$fe,$f4,$ff,$ff,$ff + DATA BYTE $fc,$ff,$ff,$ff,$f4,$ff,$ff,$ff + DATA BYTE $f4,$ff,$ff,$ff,$f4,$ff,$ff,$ff + DATA BYTE $f0,$ff,$ff,$ff,$e0,$fe,$fe,$fe + DATA BYTE $00,$fe,$fe,$fe,$00,$fe,$fe,$fe + DATA BYTE $00,$ff,$ff,$ff,$00,$fe,$fe,$fe + DATA BYTE $02,$ff,$ff,$ff,$02,$3f,$3f,$3f + DATA BYTE $00,$7f,$7f,$7f,$00,$7f,$7f,$7f + DATA BYTE $00,$3f,$3f,$3f,$00,$bf,$bf,$bf + DATA BYTE $00,$bc,$bc,$bc,$00,$b8,$b8,$b8 + DATA BYTE $00,$a0,$a0,$a0,$00,$e0,$e0,$e0 + DATA BYTE $00,$c0,$c0,$c0,$00,$46,$46,$46 + DATA BYTE $00,$ce,$ce,$ce,$00,$ee,$ee,$e6 + DATA BYTE $00,$66,$66,$66,$00,$66,$66,$66 + DATA BYTE $00,$2e,$2e,$2e,$00,$3e,$3e,$3e + DATA BYTE $00,$0f,$0f,$0f,$01,$1f,$1f,$1f + DATA BYTE $00,$08,$08,$08,$00,$18,$18,$18 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$60,$60,$60,$00,$e1,$e1,$e1 + DATA BYTE $c7,$ef,$e8,$ef,$ff,$ff,$c0,$ff + DATA BYTE $0c,$1f,$13,$0f,$00,$1f,$1f,$0f + DATA BYTE $00,$0f,$0f,$0f,$04,$0f,$0b,$0f + DATA BYTE $24,$7e,$5a,$2e,$e4,$fe,$1a,$e6 + DATA BYTE $e4,$fe,$1a,$e6,$e0,$f6,$16,$e6 + DATA BYTE $f4,$ff,$3f,$ff,$b7,$fc,$7f,$fc + DATA BYTE $97,$fc,$7f,$fe,$82,$ff,$7f,$ff + DATA BYTE $df,$ff,$3f,$ff,$cf,$ff,$3f,$ff + DATA BYTE $c0,$ff,$3f,$ff,$c0,$c0,$00,$c0 + DATA BYTE $10,$fe,$ee,$fe,$11,$fe,$ee,$fe + DATA BYTE $05,$f8,$f8,$e4,$11,$ec,$ec,$f0 + DATA BYTE $19,$64,$64,$18,$03,$1c,$1c,$18 + DATA BYTE $03,$08,$08,$08,$02,$18,$18,$00 + DATA BYTE $12,$2f,$2d,$1f,$b1,$0e,$0e,$3f + DATA BYTE $a7,$18,$18,$3f,$b6,$19,$09,$3e + DATA BYTE $9f,$70,$60,$3f,$3f,$60,$40,$7f + DATA BYTE $3c,$63,$43,$7c,$70,$ce,$8e,$70 + DATA BYTE $40,$bf,$bf,$ff,$80,$7f,$7f,$80 + DATA BYTE $00,$c0,$c0,$00,$00,$80,$80,$00 + DATA BYTE $cc,$33,$33,$fc,$80,$7f,$7f,$80 + DATA BYTE $00,$c5,$c5,$00,$28,$00,$00,$00 + DATA BYTE $00,$ff,$ff,$ff,$24,$db,$db,$ff + DATA BYTE $3d,$42,$42,$3f,$1d,$e2,$e2,$1f + DATA BYTE $1f,$e0,$e0,$1f,$1a,$e5,$e5,$1e + DATA BYTE $32,$cd,$cd,$3e,$32,$cd,$cd,$3e + DATA BYTE $00,$ff,$ff,$ff,$c0,$3f,$3f,$ff + DATA BYTE $e0,$1f,$1f,$e0,$80,$60,$60,$80 + DATA BYTE $00,$e0,$e0,$00,$00,$ff,$ff,$00 + DATA BYTE $18,$e7,$e7,$1f,$03,$fc,$fc,$07 + DATA BYTE $20,$dc,$dc,$e0,$60,$98,$98,$e0 + DATA BYTE $01,$f8,$f8,$00,$03,$38,$38,$00 + DATA BYTE $03,$18,$18,$00,$02,$08,$08,$00 + DATA BYTE $02,$f8,$f8,$00,$82,$7c,$7c,$80 + DATA BYTE $20,$7f,$7f,$7f,$04,$7f,$7f,$7f + DATA BYTE $0c,$7f,$7f,$7f,$00,$7f,$7f,$7f + DATA BYTE $00,$7f,$7f,$7f,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ef,$ef,$ef + DATA BYTE $10,$ef,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $08,$f7,$ff,$ff,$04,$f3,$f7,$f7 + DATA BYTE $10,$e3,$f3,$f3,$18,$e7,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$08,$f7,$ff,$ff + DATA BYTE $3f,$ff,$ff,$ff,$3b,$ff,$ff,$ff + DATA BYTE $1b,$ff,$ff,$ff,$0f,$ff,$ff,$ff + DATA BYTE $1f,$7f,$7f,$7f,$0f,$ff,$ff,$ff + DATA BYTE $07,$ff,$ff,$ff,$03,$ff,$ff,$ff + DATA BYTE $e4,$fe,$fe,$fe,$e0,$ff,$ff,$ff + DATA BYTE $80,$ff,$ff,$ff,$80,$ff,$ff,$ff + DATA BYTE $80,$ff,$ff,$ff,$80,$fe,$fe,$fe + DATA BYTE $80,$ed,$ed,$ed,$80,$ef,$ef,$ef + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $10,$ef,$ff,$ff,$10,$0f,$1f,$1f + DATA BYTE $00,$3f,$3f,$3f,$00,$ff,$ff,$ff + DATA BYTE $06,$4f,$4f,$4f,$0e,$df,$df,$df + DATA BYTE $02,$cf,$cf,$cf,$80,$ff,$ff,$df + DATA BYTE $00,$ff,$ff,$ff,$80,$ff,$ff,$fd + DATA BYTE $00,$ff,$ff,$ff,$40,$ff,$bf,$f7 + DATA BYTE $01,$3f,$3f,$0f,$0f,$ff,$f0,$ff + DATA BYTE $1d,$ff,$e2,$ff,$0d,$b3,$b2,$af + DATA BYTE $10,$ee,$ee,$f0,$e0,$c7,$07,$e3 + DATA BYTE $80,$e1,$61,$a1,$c0,$e3,$23,$e1 + DATA BYTE $3f,$ff,$c0,$ff,$ff,$ff,$00,$ff + DATA BYTE $fe,$ff,$01,$ff,$e0,$e0,$00,$e0 + DATA BYTE $10,$00,$00,$00,$86,$01,$01,$07 + DATA BYTE $00,$3f,$3f,$3f,$00,$ff,$ff,$ff + DATA BYTE $e0,$ff,$1f,$f7,$e0,$ff,$1f,$e7 + DATA BYTE $00,$87,$87,$07,$90,$4f,$4f,$97 + DATA BYTE $78,$87,$87,$ff,$03,$fc,$fc,$ff + DATA BYTE $07,$f8,$f8,$ff,$80,$60,$60,$80 + DATA BYTE $c0,$e0,$00,$e0,$c0,$dc,$00,$dc + DATA BYTE $c7,$ff,$00,$ff,$9f,$ff,$00,$ff + DATA BYTE $c7,$ff,$00,$ff,$ff,$ff,$00,$ff + DATA BYTE $c7,$ff,$10,$ff,$2f,$ff,$d0,$3f + DATA BYTE $02,$18,$18,$00,$08,$14,$14,$08 + DATA BYTE $0c,$f1,$f1,$ac,$1c,$e1,$e1,$fd + DATA BYTE $1c,$e1,$e1,$fd,$1c,$e3,$e3,$fd + DATA BYTE $1f,$e0,$e0,$bf,$3f,$e0,$c0,$ff + DATA BYTE $48,$f7,$b7,$f8,$71,$ff,$8e,$ff + DATA BYTE $60,$ff,$9f,$ff,$20,$ff,$df,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$80,$7f,$7f,$ff + DATA BYTE $00,$04,$04,$04,$01,$fe,$fe,$e1 + DATA BYTE $e4,$1b,$1b,$e7,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$01,$fe,$fe,$ff + DATA BYTE $03,$fc,$fc,$ff,$03,$fc,$fc,$ff + DATA BYTE $30,$cf,$cf,$3c,$32,$cd,$cd,$be + DATA BYTE $f2,$0d,$0d,$fe,$e2,$1d,$1d,$fe + DATA BYTE $c2,$3d,$3d,$fe,$c2,$3d,$3d,$fe + DATA BYTE $82,$7d,$7d,$fe,$83,$7c,$7c,$ff + DATA BYTE $00,$f7,$f7,$00,$02,$d0,$d0,$10 + DATA BYTE $50,$a0,$a0,$50,$64,$9b,$9b,$64 + DATA BYTE $7c,$83,$83,$7c,$3f,$c0,$c0,$3f + DATA BYTE $3f,$c0,$c0,$3f,$3f,$c0,$c0,$3f + DATA BYTE $00,$fc,$fc,$00,$00,$1c,$1c,$00 + DATA BYTE $00,$0d,$0d,$00,$00,$9c,$9c,$00 + DATA BYTE $00,$fc,$fc,$00,$fc,$02,$02,$fc + DATA BYTE $fc,$03,$03,$fc,$f8,$07,$07,$f8 + DATA BYTE $00,$ff,$ff,$ff,$00,$bf,$bf,$3f + DATA BYTE $20,$b3,$b3,$33,$20,$bd,$bd,$bc + DATA BYTE $00,$a7,$a7,$27,$00,$bf,$bf,$3f + DATA BYTE $00,$a3,$a3,$22,$00,$a1,$a1,$a0 + DATA BYTE $00,$f9,$f9,$f9,$08,$f7,$ff,$ff + DATA BYTE $00,$9f,$9f,$9f,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$fa,$fa,$f2 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $03,$ff,$ff,$ff,$02,$bf,$bf,$bf + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$84,$84,$84,$00,$44,$44,$44 + DATA BYTE $00,$c0,$c0,$c0,$00,$c0,$c0,$c0 + DATA BYTE $00,$ff,$ff,$ff,$00,$f7,$f7,$f7 + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$41,$41,$41,$00,$10,$10,$00 + DATA BYTE $00,$f6,$f6,$e0,$68,$7e,$76,$78 + DATA BYTE $08,$f7,$f7,$ff,$00,$af,$af,$af + DATA BYTE $02,$bf,$bd,$bf,$02,$d7,$d5,$d7 + DATA BYTE $00,$83,$83,$83,$40,$0a,$6a,$0a + DATA BYTE $40,$02,$42,$00,$01,$40,$40,$40 + DATA BYTE $ca,$ff,$75,$ff,$ce,$ff,$71,$ff + DATA BYTE $cf,$ff,$30,$ff,$c2,$ff,$3d,$ff + DATA BYTE $1c,$eb,$e3,$fe,$0c,$00,$00,$0c + DATA BYTE $43,$04,$04,$00,$f3,$00,$00,$00 + DATA BYTE $e0,$e7,$07,$e3,$e0,$e0,$00,$e0 + DATA BYTE $e0,$e1,$01,$e1,$00,$f0,$f0,$20 + DATA BYTE $00,$10,$10,$10,$03,$08,$08,$08 + DATA BYTE $e3,$08,$08,$08,$e1,$00,$08,$00 + DATA BYTE $c0,$3c,$3c,$f8,$00,$c0,$c0,$00 + DATA BYTE $00,$80,$80,$80,$3e,$00,$00,$00 + DATA BYTE $3e,$00,$00,$00,$9e,$00,$00,$00 + DATA BYTE $00,$38,$38,$30,$78,$7c,$04,$78 + DATA BYTE $00,$0e,$0e,$00,$00,$68,$68,$40 + DATA BYTE $00,$f0,$f0,$70,$00,$60,$60,$60 + DATA BYTE $00,$e0,$e0,$60,$00,$e0,$e0,$60 + DATA BYTE $00,$60,$60,$40,$00,$68,$68,$00 + DATA BYTE $ef,$ff,$10,$ff,$21,$27,$06,$27 + DATA BYTE $00,$00,$00,$00,$00,$01,$01,$01 + DATA BYTE $0c,$01,$01,$01,$de,$01,$01,$01 + DATA BYTE $de,$00,$00,$00,$1e,$01,$01,$01 + DATA BYTE $17,$88,$88,$1f,$03,$3c,$3c,$1f + DATA BYTE $43,$fc,$fc,$ff,$03,$fc,$fc,$ff + DATA BYTE $03,$9c,$9c,$9f,$13,$2c,$2c,$3f + DATA BYTE $0f,$00,$00,$0f,$03,$00,$00,$03 + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$bd,$42,$42,$ff + DATA BYTE $ff,$00,$00,$ff,$fe,$01,$01,$fe + DATA BYTE $f8,$07,$07,$f8,$f1,$0e,$0e,$f1 + DATA BYTE $27,$d8,$d8,$ff,$7e,$81,$81,$ff + DATA BYTE $fc,$03,$03,$ff,$df,$20,$20,$df + DATA BYTE $26,$d9,$d9,$3e,$44,$bb,$bb,$7c + DATA BYTE $03,$fc,$fc,$ff,$20,$df,$df,$ff + DATA BYTE $83,$7c,$7c,$ff,$03,$fc,$fc,$ff + DATA BYTE $83,$7c,$7c,$ff,$c6,$39,$39,$fe + DATA BYTE $7c,$83,$83,$7c,$38,$c6,$c6,$38 + DATA BYTE $80,$78,$78,$80,$40,$a3,$a3,$c0 + DATA BYTE $3f,$c0,$c0,$3f,$1b,$e4,$e4,$1f + DATA BYTE $5f,$a0,$a0,$5f,$0f,$f0,$f0,$0f + DATA BYTE $0f,$f0,$f0,$0f,$07,$38,$38,$07 + DATA BYTE $1f,$60,$60,$1f,$0a,$f5,$f5,$0a + DATA BYTE $f0,$0f,$0f,$f0,$f0,$0f,$0f,$f0 + DATA BYTE $f0,$0f,$0f,$f1,$e0,$1f,$1f,$e1 + DATA BYTE $e0,$1f,$1f,$e1,$c0,$3f,$3f,$c1 + DATA BYTE $c0,$3f,$3f,$c1,$02,$fd,$fd,$03 + DATA BYTE $04,$c1,$c5,$c5,$0c,$91,$9d,$9d + DATA BYTE $7c,$81,$fd,$fd,$2d,$90,$bd,$bd + DATA BYTE $3c,$81,$bd,$bd,$3e,$81,$bf,$bf + DATA BYTE $3f,$80,$bf,$bf,$1d,$80,$9d,$9d + DATA BYTE $00,$08,$08,$08,$00,$c0,$c0,$c0 + DATA BYTE $c0,$e0,$e0,$e0,$00,$e0,$e0,$e0 + DATA BYTE $30,$e1,$f1,$e1,$18,$e1,$fb,$e1 + DATA BYTE $09,$d0,$df,$d0,$04,$18,$1e,$18 + DATA BYTE $00,$e2,$e2,$e2,$02,$e1,$e1,$e2 + DATA BYTE $83,$c0,$c0,$c3,$00,$83,$83,$80 + DATA BYTE $00,$80,$80,$80,$00,$80,$80,$80 + DATA BYTE $0c,$c0,$c0,$80,$04,$31,$31,$11 + DATA BYTE $00,$7f,$7f,$79,$00,$77,$77,$77 + DATA BYTE $00,$77,$77,$77,$00,$9f,$9f,$1f + DATA BYTE $40,$8f,$8f,$0f,$00,$8f,$8f,$0f + DATA BYTE $02,$0d,$0f,$0f,$02,$0d,$0f,$0f + DATA BYTE $01,$c0,$c0,$c0,$01,$80,$80,$80 + DATA BYTE $18,$c0,$c0,$80,$00,$c0,$c0,$80 + DATA BYTE $00,$e0,$e0,$c0,$00,$c0,$c0,$c0 + DATA BYTE $00,$c0,$c0,$c0,$00,$c0,$c0,$c0 + DATA BYTE $f3,$00,$00,$00,$f3,$00,$00,$00 + DATA BYTE $f3,$00,$00,$00,$40,$24,$24,$24 + DATA BYTE $00,$7c,$7c,$7c,$00,$74,$74,$70 + DATA BYTE $00,$34,$34,$34,$00,$3f,$3f,$3f + DATA BYTE $e0,$0f,$0f,$0f,$e2,$0d,$0f,$0d + DATA BYTE $60,$0f,$0f,$0f,$00,$5f,$5f,$4f + DATA BYTE $00,$17,$17,$07,$00,$47,$47,$47 + DATA BYTE $00,$87,$87,$86,$08,$03,$03,$02 + DATA BYTE $80,$fe,$7e,$80,$80,$fe,$7e,$80 + DATA BYTE $00,$fe,$fe,$00,$00,$fe,$fe,$00 + DATA BYTE $00,$70,$70,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$01,$00,$01 + DATA BYTE $00,$00,$01,$01,$00,$00,$00,$00 + DATA BYTE $0e,$70,$70,$70,$2f,$70,$50,$70 + DATA BYTE $00,$11,$11,$11,$18,$bf,$a7,$bf + DATA BYTE $00,$b8,$b8,$b8,$00,$fc,$ec,$fc + DATA BYTE $00,$c0,$c0,$c0,$00,$80,$80,$00 + DATA BYTE $00,$80,$80,$80,$00,$80,$80,$00 + DATA BYTE $03,$00,$00,$03,$03,$80,$80,$83 + DATA BYTE $01,$c2,$c2,$c3,$01,$62,$62,$63 + DATA BYTE $01,$02,$02,$03,$00,$07,$07,$06 + DATA BYTE $00,$0f,$0f,$0e,$00,$0f,$0f,$02 + DATA BYTE $f9,$06,$06,$f9,$fc,$03,$03,$fc + DATA BYTE $ff,$00,$00,$ff,$ef,$10,$10,$ef + DATA BYTE $ff,$00,$00,$ff,$ff,$01,$00,$ff + DATA BYTE $ff,$01,$00,$ff,$7e,$81,$81,$7f + DATA BYTE $80,$7f,$7f,$ff,$00,$ff,$ff,$3f + DATA BYTE $80,$5f,$5f,$80,$20,$c7,$c7,$e0 + DATA BYTE $13,$ec,$ec,$f3,$0f,$f0,$f0,$ff + DATA BYTE $01,$fe,$fe,$ff,$0f,$f0,$f0,$ff + DATA BYTE $30,$cf,$cf,$f0,$3c,$c3,$c3,$fc + DATA BYTE $78,$87,$87,$78,$00,$ff,$ff,$00 + DATA BYTE $80,$7e,$7e,$80,$f0,$0f,$0f,$f0 + DATA BYTE $d8,$27,$27,$f8,$ff,$00,$00,$ff + DATA BYTE $08,$f7,$f7,$08,$00,$ff,$ff,$00 + DATA BYTE $00,$ff,$ff,$00,$02,$fd,$fd,$02 + DATA BYTE $06,$0b,$09,$06,$0a,$f5,$f5,$0e + DATA BYTE $88,$77,$77,$8c,$8c,$73,$73,$8c + DATA BYTE $00,$ff,$ff,$03,$00,$ff,$ff,$03 + DATA BYTE $00,$f3,$f3,$03,$00,$f3,$f3,$03 + DATA BYTE $00,$fb,$fb,$0b,$00,$fb,$fb,$0b + DATA BYTE $00,$c7,$c7,$07,$18,$c7,$c7,$07 + DATA BYTE $00,$c0,$c0,$c0,$1e,$c0,$c0,$c0 + DATA BYTE $17,$c0,$c0,$c0,$05,$c0,$c0,$c0 + DATA BYTE $05,$c0,$c0,$c0,$24,$c8,$c8,$c8 + DATA BYTE $04,$da,$da,$da,$04,$db,$db,$db + DATA BYTE $c0,$18,$1e,$18,$80,$39,$3d,$39 + DATA BYTE $84,$3a,$3e,$3a,$08,$3f,$3f,$3f + DATA BYTE $08,$3f,$3f,$3f,$10,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$71,$71,$01,$08,$71,$71,$09 + DATA BYTE $00,$f8,$f8,$80,$00,$ba,$ba,$aa + DATA BYTE $00,$fb,$fb,$fb,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$2f,$2f,$2f,$02,$21,$23,$23 + DATA BYTE $02,$01,$03,$03,$03,$00,$03,$03 + DATA BYTE $02,$c1,$c3,$c3,$00,$80,$80,$80 + DATA BYTE $38,$fc,$fc,$fc,$1d,$fc,$fd,$fd + DATA BYTE $00,$80,$80,$80,$00,$80,$80,$80 + DATA BYTE $00,$80,$80,$80,$00,$80,$80,$80 + DATA BYTE $40,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$c0,$c0,$c0,$f9,$7c,$fc,$fc + DATA BYTE $00,$1c,$1c,$1c,$00,$4e,$4e,$4e + DATA BYTE $00,$82,$82,$80,$01,$90,$90,$90 + DATA BYTE $01,$00,$00,$00,$01,$00,$00,$00 + DATA BYTE $00,$26,$26,$00,$c0,$04,$04,$00 + DATA BYTE $38,$00,$00,$00,$72,$07,$05,$06 + DATA BYTE $e0,$06,$06,$06,$00,$00,$00,$00 + DATA BYTE $bf,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $7f,$00,$00,$00,$3f,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$c0,$00,$00,$00 + DATA BYTE $e0,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $0f,$60,$60,$00,$07,$00,$00,$00 + DATA BYTE $03,$00,$00,$00,$5b,$00,$00,$00 + DATA BYTE $1b,$00,$00,$00,$1f,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $00,$03,$03,$00,$00,$01,$01,$00 + DATA BYTE $00,$01,$01,$00,$00,$00,$00,$00 + DATA BYTE $20,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$40,$40,$40,$00,$40,$40,$40 + DATA BYTE $7e,$81,$81,$7f,$3f,$c0,$c0,$3f + DATA BYTE $3c,$c3,$c3,$3f,$1c,$e3,$e3,$1f + DATA BYTE $0e,$f1,$f1,$0f,$06,$39,$39,$07 + DATA BYTE $03,$0c,$0c,$03,$41,$02,$02,$01 + DATA BYTE $0f,$f0,$f0,$ff,$02,$fd,$fd,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $80,$7f,$7f,$ff,$c0,$3f,$3f,$ff + DATA BYTE $ff,$00,$00,$ff,$ff,$00,$00,$ff + DATA BYTE $67,$98,$98,$ff,$07,$f8,$f8,$ff + DATA BYTE $0f,$f0,$f0,$ff,$0f,$f0,$f0,$ff + DATA BYTE $0f,$f0,$f0,$ff,$1f,$e0,$e0,$ff + DATA BYTE $88,$77,$77,$88,$80,$7f,$7f,$80 + DATA BYTE $00,$ff,$ff,$00,$00,$ff,$ff,$00 + DATA BYTE $00,$fe,$fe,$00,$01,$fe,$fe,$00 + DATA BYTE $01,$fc,$fc,$00,$01,$f8,$f8,$00 + DATA BYTE $38,$c3,$c3,$03,$38,$83,$83,$03 + DATA BYTE $7d,$83,$82,$03,$7d,$03,$02,$03 + DATA BYTE $fd,$03,$02,$03,$fd,$03,$02,$03 + DATA BYTE $e0,$0f,$0f,$0f,$e1,$1f,$1e,$1f + DATA BYTE $04,$db,$db,$db,$00,$db,$db,$db + DATA BYTE $80,$db,$5b,$db,$80,$fb,$7b,$fb + DATA BYTE $80,$ff,$7f,$ff,$80,$ff,$7f,$ff + DATA BYTE $80,$ff,$7f,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$01,$ff,$ff,$ff + DATA BYTE $01,$ff,$ff,$ff,$1f,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $40,$ff,$ff,$ff,$e0,$ff,$ff,$ff + DATA BYTE $f0,$ff,$ff,$ff,$f0,$ff,$ff,$ff + DATA BYTE $1c,$fe,$fe,$fe,$3f,$fe,$ff,$ff + DATA BYTE $7e,$ff,$ff,$ff,$7e,$ff,$ff,$ff + DATA BYTE $7f,$ff,$ff,$ff,$7f,$ff,$ff,$ff + DATA BYTE $fe,$ff,$ff,$ff,$fc,$fc,$fc,$fc + DATA BYTE $b8,$7c,$fc,$7c,$b8,$7c,$fc,$fc + DATA BYTE $b8,$7c,$fc,$fc,$bc,$7c,$fc,$fc + DATA BYTE $3c,$7e,$7e,$7e,$3d,$7e,$7e,$7e + DATA BYTE $3e,$3e,$3e,$3e,$1e,$3e,$3e,$3e + DATA BYTE $00,$00,$00,$00,$40,$07,$07,$00 + DATA BYTE $60,$04,$04,$00,$60,$07,$07,$04 + DATA BYTE $c2,$07,$05,$07,$c1,$2f,$2e,$2f + DATA BYTE $c2,$23,$21,$22,$c3,$23,$20,$23 + DATA BYTE $1f,$00,$00,$00,$07,$00,$00,$00 + DATA BYTE $07,$00,$00,$00,$07,$00,$00,$00 + DATA BYTE $07,$a0,$a0,$00,$07,$00,$00,$00 + DATA BYTE $07,$00,$00,$00,$07,$80,$80,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $7f,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $01,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $fe,$00,$00,$00,$fe,$00,$00,$00 + DATA BYTE $fc,$00,$00,$00,$ee,$00,$0e,$00 + DATA BYTE $3e,$00,$3f,$00,$7e,$80,$ff,$80 + DATA BYTE $40,$00,$40,$40,$80,$00,$80,$80 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$01,$01,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $04,$00,$04,$00,$02,$00,$02,$00 + DATA BYTE $01,$00,$01,$00,$00,$00,$00,$00 + DATA BYTE $60,$9f,$9f,$7f,$12,$ed,$ed,$1e + DATA BYTE $00,$1f,$1f,$04,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$80,$00,$80,$00 + DATA BYTE $20,$df,$df,$e0,$00,$ff,$ff,$80 + DATA BYTE $00,$ff,$ff,$00,$00,$06,$06,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$00,$78,$78,$00 + DATA BYTE $04,$f0,$f1,$00,$06,$c0,$c0,$00 + DATA BYTE $07,$00,$00,$00,$0f,$00,$00,$00 + DATA BYTE $07,$00,$00,$00,$03,$00,$00,$00 + DATA BYTE $05,$00,$04,$00,$07,$20,$26,$00 + DATA BYTE $61,$1f,$1e,$1f,$01,$1f,$1e,$1f + DATA BYTE $80,$1f,$1f,$1f,$e0,$03,$03,$03 + DATA BYTE $f1,$03,$03,$03,$d0,$01,$01,$01 + DATA BYTE $f0,$00,$00,$00,$e1,$00,$01,$00 + DATA BYTE $80,$ff,$7f,$ff,$40,$ff,$bf,$ff + DATA BYTE $c0,$ff,$bf,$ff,$c0,$ff,$bf,$ff + DATA BYTE $c0,$ff,$bf,$ff,$c0,$ff,$ff,$ff + DATA BYTE $40,$ff,$ff,$ff,$60,$ff,$ff,$ff + DATA BYTE $1f,$ff,$ff,$ff,$1f,$ff,$ff,$ff + DATA BYTE $3f,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $fc,$fe,$fe,$fe,$fe,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $1e,$7e,$7e,$7e,$1e,$ff,$ff,$ff + DATA BYTE $3c,$ff,$ff,$ff,$3c,$ff,$ff,$ff + DATA BYTE $1e,$ff,$ff,$ff,$10,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $41,$33,$32,$23,$00,$30,$30,$30 + DATA BYTE $01,$30,$30,$30,$05,$30,$30,$20 + DATA BYTE $07,$20,$20,$20,$03,$00,$00,$00 + DATA BYTE $06,$10,$10,$00,$06,$10,$10,$10 + DATA BYTE $02,$c0,$c0,$c0,$73,$00,$00,$00 + DATA BYTE $d2,$00,$00,$00,$d0,$00,$00,$00 + DATA BYTE $d0,$00,$00,$00,$d3,$00,$00,$00 + DATA BYTE $d8,$00,$00,$00,$49,$20,$20,$20 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $7f,$00,$00,$00,$1f,$00,$00,$00 + DATA BYTE $0e,$00,$00,$00,$0e,$00,$00,$00 + DATA BYTE $bf,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$3f,$00,$00,$00 + DATA BYTE $7f,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $fc,$00,$ff,$00,$fc,$00,$ff,$00 + DATA BYTE $e0,$00,$ff,$00,$60,$00,$ff,$00 + DATA BYTE $00,$00,$be,$00,$00,$00,$d7,$00 + DATA BYTE $00,$00,$d6,$00,$00,$00,$db,$00 + DATA BYTE $00,$00,$80,$00,$00,$00,$80,$00 + DATA BYTE $00,$00,$80,$00,$00,$00,$80,$00 + DATA BYTE $00,$00,$40,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$80,$00,$00,$00,$80,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$10,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$02,$00,$00,$00 + DATA BYTE $03,$00,$00,$00,$01,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$20,$00,$20,$00 + DATA BYTE $10,$00,$10,$00,$18,$00,$18,$00 + DATA BYTE $0c,$00,$0c,$00,$02,$00,$02,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$ff,$ff,$00,$00,$ff,$ff,$00 + DATA BYTE $00,$ff,$ff,$00,$00,$fd,$fd,$00 + DATA BYTE $00,$00,$00,$00,$00,$00,$00,$00 + DATA BYTE $00,$00,$00,$00,$20,$00,$00,$00 + DATA BYTE $05,$62,$67,$02,$04,$c3,$c7,$03 + DATA BYTE $04,$c3,$c7,$03,$04,$c3,$c7,$03 + DATA BYTE $04,$c3,$c7,$03,$04,$83,$87,$03 + DATA BYTE $00,$87,$8f,$07,$00,$07,$0f,$07 + DATA BYTE $e0,$00,$00,$00,$70,$00,$00,$00 + DATA BYTE $30,$80,$80,$80,$70,$80,$c0,$80 + DATA BYTE $38,$c0,$e0,$c0,$01,$e0,$e0,$e0 + DATA BYTE $01,$f0,$f0,$f0,$04,$f0,$f8,$f0 + DATA BYTE $a1,$7f,$ff,$7f,$01,$3f,$7f,$3f + DATA BYTE $07,$1f,$1f,$1f,$01,$1f,$1f,$1f + DATA BYTE $8b,$1f,$1f,$1f,$8f,$1f,$1f,$1f + DATA BYTE $af,$1f,$1f,$1f,$af,$1f,$1f,$1f + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$7f,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $fe,$ff,$ff,$ff,$fe,$ff,$ff,$ff + DATA BYTE $fc,$ff,$ff,$ff,$f8,$ff,$ff,$ff + DATA BYTE $f8,$ff,$ff,$ff,$fc,$ff,$ff,$ff + DATA BYTE $fc,$ff,$ff,$ff,$fc,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$01,$ff,$ff,$ff + DATA BYTE $01,$ff,$ff,$ff,$01,$ff,$ff,$ff + DATA BYTE $06,$90,$90,$90,$02,$90,$90,$90 + DATA BYTE $06,$90,$90,$90,$00,$98,$98,$98 + DATA BYTE $00,$f9,$f9,$f9,$00,$ff,$ff,$ff + DATA BYTE $80,$ff,$ff,$ff,$90,$ff,$ef,$ff + DATA BYTE $4d,$20,$20,$20,$0f,$30,$30,$30 + DATA BYTE $0f,$30,$30,$30,$0c,$30,$30,$30 + DATA BYTE $06,$f0,$f0,$f0,$04,$f0,$f0,$f0 + DATA BYTE $06,$f0,$f0,$f0,$00,$f1,$f1,$f1 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $7f,$00,$00,$00,$0d,$00,$00,$00 + DATA BYTE $29,$00,$00,$00,$55,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $fe,$00,$00,$00,$d2,$00,$00,$00 + DATA BYTE $f6,$00,$00,$00,$88,$00,$00,$00 + + ' Width = 16, height = 12 +image_pattern: + DATA BYTE $40,$00,$41,$00,$42,$00,$43,$00,$44,$00,$45,$00,$46,$00,$47,$00,$48,$00,$48,$00,$48,$00,$49,$00,$4a,$00,$4b,$00,$4c,$00,$4d,$00 + DATA BYTE $4e,$00,$4f,$00,$50,$00,$51,$00,$52,$00,$53,$00,$54,$00,$55,$00,$56,$00,$48,$00,$48,$00,$57,$00,$58,$00,$59,$00,$5a,$00,$5b,$00 + DATA BYTE $5c,$00,$5d,$00,$5e,$00,$5f,$00,$60,$00,$61,$00,$62,$00,$63,$00,$64,$00,$65,$00,$66,$00,$67,$00,$68,$00,$69,$00,$6a,$00,$6b,$00 + DATA BYTE $6c,$00,$6d,$00,$6e,$00,$6f,$00,$70,$00,$71,$00,$72,$00,$73,$00,$74,$00,$75,$00,$76,$00,$77,$00,$78,$00,$79,$00,$7a,$00,$7b,$00 + DATA BYTE $7c,$00,$7d,$00,$7e,$00,$7f,$00,$80,$00,$81,$00,$82,$00,$83,$00,$84,$00,$85,$00,$86,$00,$87,$00,$88,$00,$89,$00,$8a,$00,$8b,$00 + DATA BYTE $8c,$00,$8d,$00,$8e,$00,$8f,$00,$90,$00,$91,$00,$92,$00,$93,$00,$94,$00,$95,$00,$96,$00,$97,$00,$98,$00,$99,$00,$9a,$00,$9b,$00 + DATA BYTE $9c,$00,$9d,$00,$9e,$00,$9f,$00,$a0,$00,$a1,$00,$a2,$00,$a3,$00,$a4,$00,$a5,$00,$a6,$00,$a7,$00,$a8,$00,$a9,$00,$aa,$00,$ab,$00 + DATA BYTE $ac,$00,$ad,$00,$ae,$00,$af,$00,$b0,$00,$b1,$00,$b2,$00,$b3,$00,$b4,$00,$b5,$00,$b6,$00,$b7,$00,$b8,$00,$b9,$00,$ba,$00,$bb,$00 + DATA BYTE $bc,$00,$bd,$00,$be,$00,$bf,$00,$c0,$00,$c1,$00,$c2,$00,$c3,$00,$c4,$00,$c5,$00,$c6,$00,$c7,$00,$c8,$00,$c9,$00,$ca,$00,$cb,$00 + DATA BYTE $6d,$00,$cc,$00,$cd,$00,$ce,$00,$cf,$00,$d0,$00,$d1,$00,$d2,$00,$d3,$00,$d4,$00,$d5,$00,$d6,$00,$d7,$00,$d8,$00,$d9,$00,$da,$00 + DATA BYTE $db,$00,$dc,$00,$dd,$00,$de,$00,$df,$00,$e0,$00,$e1,$00,$e2,$00,$e3,$00,$48,$00,$e4,$00,$e5,$00,$e6,$00,$e7,$00,$e8,$00,$e9,$00 + DATA BYTE $ea,$00,$eb,$00,$ec,$00,$ed,$00,$ee,$00,$ef,$00,$f0,$00,$f1,$00,$f2,$00,$48,$00,$f3,$00,$f4,$00,$f5,$00,$f6,$00,$f7,$00,$f8,$00 diff --git a/examples/palette_msx2.bas b/examples/palette_msx2.bas new file mode 100644 index 0000000..a2963d1 --- /dev/null +++ b/examples/palette_msx2.bas @@ -0,0 +1,111 @@ + ' + ' MSX2 Palette demo + ' + ' by Oscar Toledo G. (example for CVBasic) + ' https://nanochess.org/ + ' + ' Creation date: Feb/09/2026. + ' + + ' + ' The RGB (red-green-blue) color order inside the 16-bit word is: + ' 0 0 0 0 0 g g g 0 r r r 0 b b b + ' + + MODE 4 + + DEFINE SPRITE 0, 1, happy_face + DEFINE SPRITE COLOR 0,1,sprite_color + + PRINT AT 33,"Hello world!" + + PALETTE LOAD main_palette + + FOR #c = $0400 TO $043F + VPOKE #c, $ff + NEXT #c + + d = $41 + FOR #c = $2400 TO $2438 STEP 8 + VPOKE #c, d + VPOKE #c + 1, d + VPOKE #c + 2, d + VPOKE #c + 3, d + VPOKE #c + 4, d + VPOKE #c + 5, d + VPOKE #c + 6, d + VPOKE #c + 7, d + d = d + $10 + NEXT #c + + e = $80 + FOR #c = $1810 TO $181E STEP 2 + FOR d = 0 TO 7 + VPOKE #c + d * 32, e + VPOKE #c + d * 32 + 1, e + NEXT d + e = e + 1 + NEXT #c + +main_loop: + SPRITE 0, $60, $40, 0 + + FOR #c = $0010 TO $0070 STEP $0010 + WAIT + WAIT + PALETTE 15, #c + NEXT #c + + FOR #c = $0100 TO $0700 STEP $0100 + WAIT + WAIT + PALETTE 15, #c + NEXT #c + + FOR #c = $0001 TO $0007 STEP $0001 + WAIT + WAIT + PALETTE 15, #c + NEXT #c + + GOTO main_loop + +main_palette: + DATA $0000 + DATA $0462 + DATA $0777 + DATA $0777 + DATA $0000 + DATA $0001 + DATA $0012 + DATA $0123 + DATA $0234 + DATA $0345 + DATA $0456 + DATA $0567 + DATA $0374 + DATA $0777 + DATA $0777 + DATA $0777 + +happy_face: + BITMAP "......XXXX......" + BITMAP "....XXXXXXXX...." + BITMAP "...XXXXXXXXXX..." + BITMAP "..XXXXXXXXXXXX.." + BITMAP ".XXXXXXXXXXXXXX." + BITMAP ".XXXXXXXXXXXXXX." + BITMAP "XXXX..XXXX..XXXX" + BITMAP "XXXX..XXXX..XXXX" + BITMAP "XXXXXXXXXXXXXXXX" + BITMAP "XXXXXXXXXXXXXXXX" + BITMAP ".XXXXXXXXXXXXXX." + BITMAP ".XXX..XXXX..XXX." + BITMAP "..XXX......XXX.." + BITMAP "...XXX....XXX..." + BITMAP "....XXXXXXXX...." + BITMAP "......XXXX......" + +sprite_color: + DATA BYTE $0B,$0B,$0A,$0A,$09,$09,$08,$08 + DATA BYTE $07,$07,$06,$06,$05,$05,$04,$04 diff --git a/examples/plot.bas b/examples/plot.bas new file mode 100644 index 0000000..aef44e1 --- /dev/null +++ b/examples/plot.bas @@ -0,0 +1,168 @@ + ' + ' Drawing pixels and lines with CVBasic + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Jan/23/2025. + ' + + DEF FN PSET(x,y)=#c=((x) AND $f8.)+((y) / 8. * 256)+((y) AND 7.):VPOKE #c,VPEEK(#c) OR bit_table((x) AND 7) + + DEF FN PSETCOLOR(x,y,color)=PSET(x,y):VPOKE #c+$2000,color*16 + + DEF FN PRESET(x,y)=#c=((x) AND $f8.)+((y) / 8. * 256)+((y) AND 7.):VPOKE #c,VPEEK(#c) AND NOT bit_table((x) AND 7) + + DEF FN LINE(w,x,y,z)=x1=w:y1=x:x2=y:y2=z:GOSUB draw_line + + DEF FN LINECOLOR(w,x,y,z,a)=x1=w:y1=x:x2=y:y2=z:c=a:GOSUB draw_line_color + + DEF FN CIRCLE(w,x,y)=x1=w:y1=x:c=y:GOSUB draw_circle + + MODE 1 + + y = 20 + FOR x = 20 TO 220 + PSET(x, y) + NEXT x + + FOR y = 20 TO 170 + PSET(x, y) + NEXT y + + FOR x = 220 TO 20 STEP -1 + PSET(x, y) + NEXT x + + FOR y = 170 TO 20 STEP -1 + PSET(x, y) + NEXT y + + FOR y = 40 TO 150 + FOR x = 32 TO 55 + PSETCOLOR(x, y, y AND $0F) + NEXT x + NEXT y + + FOR x = 102 TO 202 STEP 10 + LINE(x, 40, 202, 150) + NEXT x + + FOR y = 40 TO 150 STEP 10 + LINE(102, y, 202, 150) + NEXT y + + FOR x = 8 TO 240 STEP 8 + LINECOLOR(128, 0, x, 16, (x / 8) AND $0F) + NEXT x + + CIRCLE(80, 60, 15) + CIRCLE(80, 100, 20) + CIRCLE(80, 140, 25) + + WHILE 1: WEND + + ' + ' Draw a line using the Bresenham algorithm + ' + SIGNED #err + +draw_line: PROCEDURE + sx = SGN(x2 + 0 - x1) + sy = SGN(y2 + 0 - y1) + #dx = ABS(x2 + 0 - x1) + #dy = ABS(y2 + 0 - y1) + IF #dx > #dy THEN + #err = 2 * #dy - #dx + WHILE 1 + PSET(x1, y1) + IF x1 = x2 THEN RETURN + IF #err < 0 THEN + #err = #err + 2 * #dy + ELSE + #err = #err + 2 * (#dy - #dx) + y1 = y1 + sy + END IF + x1 = x1 + sx + WEND + ELSE + #err = 2 * #dx - #dy + WHILE 1 + PSET(x1, y1) + IF y1 = y2 THEN RETURN + IF #err < 0 THEN + #err = #err + 2 * #dx + ELSE + #err = #err + 2 * (#dx - #dy) + x1 = x1 + sx + END IF + y1 = y1 + sy + WEND + END IF + END + +draw_line_color: PROCEDURE + sx = SGN(x2 + 0 - x1) + sy = SGN(y2 + 0 - y1) + #dx = ABS(x2 + 0 - x1) + #dy = ABS(y2 + 0 - y1) + IF #dx > #dy THEN + #err = 2 * #dy - #dx + WHILE 1 + PSETCOLOR(x1, y1, c) + IF x1 = x2 THEN RETURN + IF #err < 0 THEN + #err = #err + 2 * #dy + ELSE + #err = #err + 2 * (#dy - #dx) + y1 = y1 + sy + END IF + x1 = x1 + sx + WEND + ELSE + #err = 2 * #dx - #dy + WHILE 1 + PSETCOLOR(x1, y1, c) + IF y1 = y2 THEN RETURN + IF #err < 0 THEN + #err = #err + 2 * #dx + ELSE + #err = #err + 2 * (#dx - #dy) + x1 = x1 + sx + END IF + y1 = y1 + sy + WEND + END IF + END + +draw_points: PROCEDURE + PSET(x1 + x, y1 + y) + PSET(x1 + x, y1 - y) + PSET(x1 - x, y1 + y) + PSET(x1 - x, y1 - y) + PSET(x1 + y, y1 + x) + PSET(x1 + y, y1 - x) + PSET(x1 - y, y1 + x) + PSET(x1 - y, y1 - x) + END + +draw_circle: PROCEDURE + x = 0 + y = c + #err = 3 - 2 * c + GOSUB draw_points + WHILE x <= y + IF #err > 0 THEN + y = y - 1 + #err = #err + 4 * x - 4 * y + 10 + ELSE + #err = #err + 4 * x + 6 + END IF + x = x + 1 + GOSUB draw_points + WEND + + END + +bit_table: + DATA BYTE $80,$40,$20,$10,$08,$04,$02,$01 diff --git a/examples/portrait_sms.bas b/examples/portrait_sms.bas new file mode 100644 index 0000000..3039d58 --- /dev/null +++ b/examples/portrait_sms.bas @@ -0,0 +1,271 @@ + ' TMSColor 3.0 Apr/27/2025 + ' Command: ../tmscolor/tmscolor -sms -b -n -t128 portrait.bmp portrait_sms.bas portrait + ' Created: Sun Apr 27 01:02:39 2025 + + ' + ' Recommended code: + ' MODE 4 + ' DEFINE CHAR 128,62,portrait_char + ' SCREEN portrait_pattern,0,0,8,8,8 + ' + ' Start tile = 128. Total_tiles = 62 +portrait_char: + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$f8,$00,$07,$07 + DATA BYTE $c0,$00,$3f,$3f,$00,$00,$ff,$ff + DATA BYTE $01,$01,$fe,$fe,$33,$33,$cc,$cc + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$1f,$00,$e0,$e0 + DATA BYTE $1f,$00,$e0,$e0,$0f,$00,$f0,$f0 + DATA BYTE $0f,$00,$f0,$f0,$07,$00,$f8,$f8 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$fe,$01,$00,$01 + DATA BYTE $fc,$03,$00,$03,$f8,$07,$00,$07 + DATA BYTE $f0,$0f,$00,$0f,$e0,$1f,$00,$1f + DATA BYTE $ff,$3f,$00,$3f,$f0,$7f,$00,$7f + DATA BYTE $00,$ff,$00,$ff,$00,$ff,$00,$ff + DATA BYTE $00,$ff,$00,$ff,$00,$ff,$00,$ff + DATA BYTE $00,$ff,$00,$ff,$00,$ff,$00,$ff + DATA BYTE $ff,$ff,$00,$ff,$1f,$ff,$00,$ff + DATA BYTE $2f,$ff,$00,$ff,$07,$ff,$00,$ff + DATA BYTE $07,$ff,$00,$ff,$01,$ff,$00,$ff + DATA BYTE $00,$ff,$00,$ff,$00,$ff,$00,$ff + DATA BYTE $ff,$00,$00,$00,$ff,$80,$00,$80 + DATA BYTE $ff,$e0,$00,$e0,$ff,$f0,$00,$f0 + DATA BYTE $ff,$f8,$00,$f8,$ff,$fc,$00,$fc + DATA BYTE $ff,$fe,$00,$fe,$fb,$fe,$00,$fe + DATA BYTE $11,$ee,$00,$00,$11,$ee,$00,$00 + DATA BYTE $ff,$00,$00,$00,$11,$00,$ee,$ee + DATA BYTE $11,$00,$ee,$ee,$ff,$00,$00,$00 + DATA BYTE $11,$00,$ee,$ee,$11,$00,$ee,$ee + DATA BYTE $11,$00,$ee,$ee,$11,$00,$ee,$ee + DATA BYTE $ff,$00,$00,$00,$11,$ee,$00,$00 + DATA BYTE $11,$ee,$00,$00,$ff,$00,$00,$00 + DATA BYTE $11,$00,$ee,$ee,$11,$00,$ee,$ee + DATA BYTE $60,$60,$9f,$9f,$00,$00,$ff,$ff + DATA BYTE $00,$00,$ff,$ff,$3f,$3f,$c0,$c0 + DATA BYTE $60,$60,$9f,$9f,$00,$00,$ff,$ff + DATA BYTE $01,$01,$fe,$fe,$37,$37,$c8,$c8 + DATA BYTE $07,$00,$f8,$f8,$07,$00,$f8,$f8 + DATA BYTE $07,$00,$f8,$f8,$07,$00,$f8,$f8 + DATA BYTE $07,$00,$f8,$f8,$07,$00,$f8,$f8 + DATA BYTE $07,$00,$f8,$f8,$07,$00,$f8,$f8 + DATA BYTE $c0,$3f,$00,$3f,$c0,$3f,$00,$3f + DATA BYTE $e0,$7f,$00,$7f,$fe,$7f,$00,$7f + DATA BYTE $ff,$ff,$00,$ff,$ff,$ff,$00,$ff + DATA BYTE $ff,$ff,$00,$ff,$ff,$ff,$20,$ff + DATA BYTE $00,$ff,$00,$ff,$0e,$ff,$00,$ff + DATA BYTE $0f,$ff,$00,$ff,$7f,$ff,$00,$ff + DATA BYTE $ff,$ff,$00,$ff,$ff,$ff,$00,$ff + DATA BYTE $ff,$ff,$00,$ff,$fe,$ff,$00,$ff + DATA BYTE $00,$ff,$00,$ff,$40,$ff,$00,$ff + DATA BYTE $c0,$ff,$00,$ff,$d0,$ff,$00,$ff + DATA BYTE $f0,$ff,$00,$ff,$f0,$ff,$00,$ff + DATA BYTE $f8,$ff,$00,$ff,$fc,$ff,$00,$ff + DATA BYTE $ff,$fe,$00,$fe,$7d,$ff,$00,$ff + DATA BYTE $7d,$ff,$00,$ff,$1f,$ff,$00,$ff + DATA BYTE $9e,$ff,$00,$ff,$ae,$ff,$00,$ff + DATA BYTE $a7,$ff,$00,$ff,$87,$ff,$00,$ff + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$80,$00,$80 + DATA BYTE $ff,$80,$00,$80,$ff,$80,$00,$80 + DATA BYTE $ff,$80,$00,$80,$ff,$c0,$00,$c0 + DATA BYTE $ff,$00,$00,$00,$11,$ee,$00,$00 + DATA BYTE $11,$ee,$00,$00,$ff,$00,$00,$00 + DATA BYTE $11,$00,$ee,$ee,$11,$00,$ee,$ee + DATA BYTE $ff,$00,$00,$00,$11,$00,$ee,$ee + DATA BYTE $28,$28,$d7,$d7,$00,$00,$ff,$ff + DATA BYTE $00,$00,$ff,$ff,$07,$00,$f8,$f8 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $0f,$00,$f0,$f0,$3f,$00,$c0,$c0 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$ff,$7f,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$fe,$ff + DATA BYTE $ff,$ff,$00,$ff,$ff,$ff,$00,$ff + DATA BYTE $ff,$ff,$00,$ff,$ff,$ff,$00,$ff + DATA BYTE $ff,$ff,$88,$ff,$ff,$ff,$98,$ff + DATA BYTE $ff,$ff,$9c,$ff,$bf,$80,$00,$c0 + DATA BYTE $dc,$ff,$00,$ff,$5c,$ff,$00,$ff + DATA BYTE $5a,$ff,$00,$ff,$fa,$ff,$00,$ff + DATA BYTE $fe,$ff,$00,$ff,$ff,$ff,$00,$ff + DATA BYTE $87,$87,$00,$ff,$07,$07,$00,$ff + DATA BYTE $43,$ff,$00,$ff,$e3,$ff,$00,$ff + DATA BYTE $63,$ff,$00,$ff,$6b,$ff,$00,$ff + DATA BYTE $6b,$ff,$00,$ff,$2b,$ff,$00,$ff + DATA BYTE $2b,$ff,$00,$ff,$2b,$ff,$00,$ff + DATA BYTE $7f,$c0,$00,$c0,$7f,$c0,$00,$c0 + DATA BYTE $7f,$c0,$00,$c0,$7f,$c0,$00,$c0 + DATA BYTE $7f,$c0,$00,$c0,$7f,$c0,$00,$c0 + DATA BYTE $3f,$c0,$00,$c0,$3f,$c0,$00,$c0 + DATA BYTE $11,$00,$ee,$ee,$ff,$00,$00,$00 + DATA BYTE $11,$ee,$00,$00,$11,$ee,$00,$00 + DATA BYTE $ff,$00,$00,$00,$11,$00,$ee,$ee + DATA BYTE $11,$00,$ee,$ee,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$e0,$00,$1f,$1f + DATA BYTE $00,$00,$ff,$ff,$00,$00,$ff,$ff + DATA BYTE $00,$00,$ff,$ff,$0e,$0e,$f1,$f1 + DATA BYTE $70,$70,$8f,$8f,$00,$00,$ff,$ff + DATA BYTE $ff,$00,$00,$00,$0f,$00,$f0,$f0 + DATA BYTE $07,$00,$f8,$f8,$07,$00,$f8,$f8 + DATA BYTE $07,$00,$f8,$f8,$07,$00,$f8,$f8 + DATA BYTE $07,$00,$f8,$f8,$07,$00,$f8,$f8 + DATA BYTE $ff,$e0,$80,$e0,$80,$e0,$00,$ff + DATA BYTE $9f,$e0,$00,$ff,$9f,$e0,$00,$ff + DATA BYTE $9f,$e0,$00,$ff,$9f,$e0,$00,$ff + DATA BYTE $8f,$f0,$00,$ff,$cf,$f0,$00,$ff + DATA BYTE $00,$00,$00,$ff,$3f,$00,$00,$ff + DATA BYTE $3f,$00,$00,$ff,$3f,$00,$00,$ff + DATA BYTE $3f,$00,$00,$ff,$0f,$00,$00,$ff + DATA BYTE $0f,$00,$00,$ff,$1f,$00,$00,$ff + DATA BYTE $07,$07,$00,$ff,$87,$07,$00,$ff + DATA BYTE $87,$07,$00,$ff,$c7,$07,$00,$ff + DATA BYTE $c7,$07,$00,$ff,$c7,$07,$00,$ff + DATA BYTE $c7,$07,$00,$ff,$c7,$07,$00,$ff + DATA BYTE $ab,$ff,$00,$ff,$a3,$ff,$00,$ff + DATA BYTE $a3,$ff,$00,$ff,$a3,$ff,$00,$ff + DATA BYTE $a5,$ff,$00,$ff,$ad,$ff,$00,$ff + DATA BYTE $ad,$ff,$00,$ff,$ad,$ff,$00,$ff + DATA BYTE $7f,$c0,$00,$c0,$ff,$c0,$00,$c0 + DATA BYTE $ff,$c0,$00,$c0,$ff,$c0,$00,$c0 + DATA BYTE $ff,$c0,$00,$c0,$ff,$c0,$00,$c0 + DATA BYTE $ff,$c0,$00,$c0,$ff,$c0,$00,$c0 + DATA BYTE $02,$02,$fd,$fd,$7e,$7e,$81,$81 + DATA BYTE $00,$00,$ff,$ff,$00,$00,$ff,$ff + DATA BYTE $3f,$3f,$c0,$c0,$00,$00,$ff,$ff + DATA BYTE $00,$00,$ff,$ff,$00,$00,$ff,$ff + DATA BYTE $07,$00,$f8,$f8,$07,$00,$f8,$f8 + DATA BYTE $07,$00,$f8,$f8,$07,$00,$f8,$f8 + DATA BYTE $07,$00,$f8,$f8,$07,$00,$f8,$f8 + DATA BYTE $1f,$00,$e0,$e0,$7f,$00,$80,$80 + DATA BYTE $c7,$f8,$00,$ff,$c3,$fc,$00,$ff + DATA BYTE $c3,$fc,$00,$ff,$c1,$fe,$00,$ff + DATA BYTE $c0,$ff,$00,$ff,$c0,$ff,$00,$ff + DATA BYTE $c0,$ff,$00,$ff,$c0,$ff,$80,$ff + DATA BYTE $ff,$00,$00,$ff,$ff,$00,$00,$ff + DATA BYTE $07,$f8,$f8,$07,$07,$f8,$f8,$07 + DATA BYTE $8f,$70,$70,$8f,$ff,$00,$00,$fc + DATA BYTE $ff,$80,$00,$fc,$bf,$c0,$00,$fc + DATA BYTE $c7,$07,$00,$ff,$c7,$07,$00,$ff + DATA BYTE $87,$07,$00,$ff,$8f,$07,$00,$f7 + DATA BYTE $9f,$07,$00,$e7,$b7,$07,$01,$4f + DATA BYTE $e7,$07,$01,$1f,$87,$07,$03,$7f + DATA BYTE $af,$ff,$00,$ff,$af,$ff,$00,$ff + DATA BYTE $af,$ff,$00,$ff,$af,$ff,$00,$ff + DATA BYTE $af,$ff,$00,$ff,$af,$ff,$00,$ff + DATA BYTE $af,$ff,$00,$ff,$ef,$ff,$00,$ff + DATA BYTE $7f,$c0,$00,$c0,$7f,$c0,$00,$c0 + DATA BYTE $7f,$c0,$00,$c0,$7f,$c0,$00,$c0 + DATA BYTE $7f,$c0,$00,$c0,$7f,$c0,$00,$c0 + DATA BYTE $7f,$c0,$00,$c0,$7f,$c0,$00,$c0 + DATA BYTE $ff,$00,$00,$00,$11,$00,$ee,$ee + DATA BYTE $11,$00,$ee,$ee,$ff,$00,$00,$00 + DATA BYTE $11,$00,$ee,$ee,$11,$00,$ee,$ee + DATA BYTE $ff,$00,$00,$00,$11,$ee,$00,$00 + DATA BYTE $03,$00,$fc,$fc,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $c0,$ff,$80,$ff,$c0,$ff,$80,$ff + DATA BYTE $c0,$ff,$80,$ff,$c0,$ff,$80,$ff + DATA BYTE $c0,$ff,$80,$ff,$c0,$ff,$80,$ff + DATA BYTE $c0,$ff,$80,$ff,$c0,$ff,$80,$ff + DATA BYTE $bf,$c0,$00,$ff,$80,$e0,$00,$ff + DATA BYTE $87,$fc,$00,$ff,$87,$fc,$00,$ff + DATA BYTE $87,$fc,$00,$ff,$8b,$f4,$00,$ff + DATA BYTE $ff,$00,$fc,$ff,$ff,$00,$fc,$ff + DATA BYTE $07,$07,$03,$ff,$07,$07,$03,$ff + DATA BYTE $07,$07,$03,$ff,$87,$07,$03,$ff + DATA BYTE $87,$07,$02,$ff,$e7,$07,$02,$ff + DATA BYTE $ff,$07,$02,$ff,$bf,$07,$02,$ff + DATA BYTE $ed,$ff,$00,$ff,$ef,$ff,$00,$ff + DATA BYTE $ef,$ff,$00,$ff,$ed,$ff,$00,$ff + DATA BYTE $cd,$ff,$00,$ff,$cc,$ff,$00,$ff + DATA BYTE $8c,$ff,$00,$ff,$8c,$ff,$00,$ff + DATA BYTE $7f,$c0,$00,$c0,$7f,$c0,$00,$c0 + DATA BYTE $7f,$c0,$00,$c0,$3f,$c0,$00,$c0 + DATA BYTE $3f,$c0,$00,$c0,$1f,$e0,$00,$e0 + DATA BYTE $1f,$e0,$00,$e0,$1f,$e0,$00,$e0 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$01,$01,$01,$ff,$0f,$0f,$0f + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$02,$02,$02,$ff,$0f,$0f,$0f + DATA BYTE $ff,$3f,$3f,$3f,$ff,$7f,$7f,$7f + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $c0,$ff,$80,$ff,$c0,$ff,$80,$ff + DATA BYTE $c0,$ff,$80,$ff,$c1,$fe,$81,$ff + DATA BYTE $e1,$fe,$81,$ff,$e1,$ff,$81,$ff + DATA BYTE $e3,$ff,$83,$ff,$e3,$ff,$83,$ff + DATA BYTE $ff,$00,$fc,$ff,$7e,$80,$7e,$ff + DATA BYTE $7f,$80,$7f,$ff,$ff,$03,$ff,$ff + DATA BYTE $ff,$03,$ff,$ff,$ff,$03,$ff,$ff + DATA BYTE $ff,$03,$ff,$ff,$ff,$02,$ff,$ff + DATA BYTE $bf,$07,$02,$ff,$3f,$07,$00,$ff + DATA BYTE $7f,$07,$00,$ff,$3f,$07,$00,$ff + DATA BYTE $ff,$07,$f8,$ff,$ff,$07,$f8,$ff + DATA BYTE $fb,$07,$f8,$ff,$fb,$07,$f8,$ff + DATA BYTE $8c,$ff,$00,$ff,$8c,$ff,$00,$ff + DATA BYTE $8c,$ff,$00,$ff,$88,$ff,$00,$ff + DATA BYTE $83,$ff,$03,$ff,$87,$ff,$07,$ff + DATA BYTE $0f,$ff,$0f,$ff,$3f,$ff,$3f,$ff + DATA BYTE $1f,$e0,$00,$e0,$3f,$ff,$3f,$ff + DATA BYTE $7f,$ff,$7f,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$00,$00,$00,$ff,$00,$00,$00 + DATA BYTE $ff,$e0,$e0,$e0,$ff,$f8,$f8,$f8 + DATA BYTE $ff,$fc,$fc,$fc,$ff,$fe,$fe,$fe + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$fe,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$3f,$ff,$ff,$ff + DATA BYTE $3f,$ff,$ff,$ff,$3f,$ff,$ff,$ff + DATA BYTE $7f,$ff,$ff,$ff,$7f,$ff,$ff,$ff + DATA BYTE $c3,$ff,$83,$ff,$c3,$ff,$c3,$ff + DATA BYTE $ff,$fe,$ff,$ff,$ff,$fe,$ff,$ff + DATA BYTE $01,$fe,$ff,$ff,$01,$fe,$ff,$ff + DATA BYTE $03,$fc,$ff,$ff,$07,$f8,$ff,$ff + DATA BYTE $ff,$04,$ff,$ff,$ff,$00,$ff,$ff + DATA BYTE $ff,$00,$ff,$ff,$ff,$00,$ff,$ff + DATA BYTE $ff,$00,$ff,$ff,$ff,$00,$ff,$ff + DATA BYTE $ff,$01,$ff,$ff,$ff,$03,$ff,$ff + DATA BYTE $fe,$03,$fc,$ff,$fc,$03,$fc,$ff + DATA BYTE $ff,$0f,$ff,$ff,$ff,$1f,$ff,$ff + DATA BYTE $ff,$7f,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$fe,$ff,$ff,$ff + DATA BYTE $3f,$ff,$3f,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$fc,$ff,$ff,$ff + DATA BYTE $f0,$ff,$ff,$ff,$c0,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $f3,$ff,$ff,$ff,$c1,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $00,$ff,$ff,$ff,$00,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + DATA BYTE $ff,$ff,$ff,$ff,$3f,$ff,$ff,$ff + DATA BYTE $1f,$ff,$ff,$ff,$1f,$ff,$ff,$ff + DATA BYTE $0f,$ff,$ff,$ff,$0f,$ff,$ff,$ff + + ' Width = 8, height = 8 +portrait_pattern: + DATA BYTE $80,$00,$81,$00,$82,$00,$83,$00,$84,$00,$85,$00,$86,$00,$87,$00 + DATA BYTE $88,$00,$89,$00,$8a,$00,$8b,$00,$8c,$00,$8d,$00,$8e,$00,$8f,$00 + DATA BYTE $90,$00,$91,$00,$92,$00,$93,$00,$94,$00,$95,$00,$96,$00,$97,$00 + DATA BYTE $98,$00,$99,$00,$9a,$00,$9b,$00,$9c,$00,$9d,$00,$9e,$00,$86,$04 + DATA BYTE $9f,$00,$a0,$00,$a1,$00,$a2,$00,$a3,$00,$a4,$00,$a5,$00,$a6,$00 + DATA BYTE $a7,$00,$a8,$00,$a9,$00,$aa,$00,$ab,$00,$ac,$00,$ad,$00,$a6,$04 + DATA BYTE $ae,$00,$af,$00,$b0,$00,$b1,$00,$b2,$00,$b3,$00,$b4,$00,$b5,$00 + DATA BYTE $b6,$00,$b7,$00,$b8,$00,$b9,$00,$ba,$00,$bb,$00,$bc,$00,$bd,$00 diff --git a/examples/space_attack_nes.bas b/examples/space_attack_nes.bas new file mode 100644 index 0000000..6222aac --- /dev/null +++ b/examples/space_attack_nes.bas @@ -0,0 +1,561 @@ + ' + ' Space Attack (demo for CVBasic) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Feb/29/2024. + ' Revision date: Nov/13/2024. Added pixel stars background and double-speed enemies. + ' Revision date: Aug/24/2025. Adapted for NES/Famicom. + ' + + DIM enemy_x(8) + DIM enemy_y(8) + DIM enemy_s(8) + +restart_game: + CLS + PALETTE LOAD game_palette + + #score = 0 + + player_x = 120 + player_y = 192 + bullet_y = 0 + FOR c = 0 TO 7 + enemy_s(c) = 0 + NEXT c + + SCREEN DISABLE + + ' Draw the stars background + ' + ' We make a vertical strip using 8 characters (numbers 16 to 23) + ' and we repeat it continuously (24 rows / 8 characters = 3 repetitions) + ' + FOR d = 0 TO 31 ' For each column + FOR #c = 0 TO 928 STEP 32 + e = RANDOM(8) + 16 + VPOKE $2000 + #c + d, e ' Put strip character. + VPOKE $2800 + #c + d, e ' Put strip character. + NEXT #c + NEXT d + + SCREEN ENABLE + + GOSUB update_score + +game_loop: + WAIT + + ' + ' Displace stars pixel by pixel + ' + scroll_y = scroll_y - 1 + IF scroll_y = $ff THEN scroll_y = $ef + SCROLL 0, scroll_y + + ' Background "music" (two tones alternating each 16 video frames) + #c = 960 + IF FRAME AND 32 THEN #c = 1023 + d = (FRAME AND 31) / 2 + SOUND 10, #c, 15 - d + 16 + + ' Setup player sprite + SPRITE 0,player_y-1,player_x,0+1,0 + SPRITE 1,player_y-1,player_x+8,2+1,0 + + ' Setup bullet sprite + IF bullet_y = 0 THEN ' Active? + SPRITE 2,$f0,0,0,0 ' No, remove sprites. + SPRITE 3,$f0,0,0,0 + SOUND 11,,16 ' Disable sound. + ELSE + SPRITE 2,bullet_y-1,bullet_x,8+1,2 ' Setup sprites. + SPRITE 3,bullet_y-1,bullet_x+8,10+1,2 ' + bullet_y = bullet_y - 4 ' Displace bullet. + SOUND 11,bullet_y+16,11+16 ' Make sound. + END IF + + ' + ' Display and move the enemies. + ' + FOR c = 0 TO 7 + IF enemy_s(c) = 0 THEN ' No enemy + SPRITE 4 + c * 2, $f0, 0, 0, 0 + SPRITE 5 + c * 2, $f0, 0, 0, 0 + ' Create one + enemy_x(c) = RANDOM(232) + 4 + enemy_y(c) = $f0 + RANDOM(16) + enemy_s(c) = RANDOM(2) + 1 + ELSEIF enemy_s(c) < 3 THEN ' Enemy moving. + SPRITE 4 + c * 2, enemy_y(c) - 1, enemy_x(c), 4+1, 1 + SPRITE 5 + c * 2, enemy_y(c) - 1, enemy_x(c) + 8, 6+1, 1 + + ' Slowly drift towards the player. + IF (FRAME AND 3) = 0 THEN + IF player_x < enemy_x(c) THEN + enemy_x(c) = enemy_x(c) - 1 + ELSE + enemy_x(c) = enemy_x(c) + 1 + END IF + END IF + ' Move down. + IF enemy_s(c) = 1 THEN + enemy_y(c) = enemy_y(c) + 2 + ELSE + enemy_y(c) = enemy_y(c) + 3 + END IF + IF enemy_y(c) >= $e8 AND enemy_y(c) <= $ef THEN ' Reached bottom. + enemy_x(c) = RANDOM(168) + 4 + enemy_y(c) = $f2 ' Reset enemy. + enemy_s(c) = RANDOM(2) + 1 + END IF + + ' + ' Check if bullet has been launched. + ' + IF bullet_y <> 0 THEN ' Is bullet launched? + IF ABS(bullet_x + 1 - enemy_x(c)) < 8 THEN + IF ABS(bullet_y + 1 - enemy_y(c)) < 8 THEN + enemy_s(c) = 3 ' Enemy explodes + #score = #score + 1 + GOSUB update_score + bullet_y = 0 + SOUND 14,$E7,13+16 ' Start enemy explosion sound + END IF + END IF + END IF + + ' + ' Check if player is hit by enemy. + ' + IF ABS(player_y + 1 - enemy_y(c)) < 8 THEN + IF ABS(player_x + 1 - enemy_x(c)) < 8 THEN + GOTO player_dies + END IF + END IF + ELSE + ' Enemy explosion. + IF FRAME AND 4 THEN + SPRITE 4 + c * 2, $f0, 0, 0, 0 + SPRITE 5 + c * 2, $f0, 0, 0, 0 + ELSE + SPRITE 4 + c * 2, enemy_y(c) - 1, enemy_x(c), 12+1, 3 + SPRITE 5 + c * 2, enemy_y(c) - 1, enemy_x(c) + 8, 14+1, 3 + END IF + + ' Displace explosion slowly. + IF FRAME AND 1 THEN + IF enemy_y(c) < $c0 THEN enemy_y(c) = enemy_y(c) + 1 + END IF + + ' Explosion sound. + SOUND 14,enemy_s(c) + enemy_s(c) = enemy_s(c) + 1 + IF enemy_s(c) = 80 THEN ' Time reached. + SOUND 14,,0+16 + enemy_x(c) = RANDOM(232) + 4 + enemy_y(c) = $f2 + enemy_s(c) = 1 ' Bring back enemy. + END IF + END IF + NEXT c + + ' + ' Movement of the player. + ' + IF cont1.left THEN IF player_x > 0 THEN player_x = player_x - 2 + IF cont1.right THEN IF player_x < 240 THEN player_x = player_x + 2 + IF cont1.button THEN ' Fire! + IF bullet_y = 0 THEN ' Only if no bullet active. + bullet_y = player_y - 8 + bullet_x = player_x + END IF + END IF + GOTO game_loop + + ' + ' Player dies. + ' +player_dies: + ' + ' Explosion effect and sound. + ' + SOUND 10,,0+16 + SOUND 11,,0+16 + FOR c = 0 TO 120 + WAIT + SOUND 14,$E4 + (c AND 3),13+16 + e = player_y - 1 + RANDOM(5) - 2 + d = player_x + RANDOM(5) - 2 + SPRITE 0, e, d, 12+1, 3 + SPRITE 1, e, d + 8, 14+1, 3 + NEXT c + SOUND 14,,0+16 + + ' + ' Remove enemies. + ' + FOR c = 2 TO 19 + SPRITE c, $f0, 0, 0, 0 + NEXT c + + CLS + SCROLL 0,0 + WAIT + + PRINT AT 250,"GAME" + PRINT AT 282,"OVER" + + ' + ' Big delay. + ' + #c = FRAME + DO + WAIT + LOOP WHILE FRAME - #c < 300 + + GOTO restart_game + + ' + ' Update the score on the screen. + ' +update_score: PROCEDURE + SPRITE 32, 16, $c0, #score / 10000 % 10 * 2 + 128, 0 + SPRITE 33, 16, $c8, #score / 1000 % 10 * 2 + 128, 0 + SPRITE 34, 16, $d0, #score / 100 % 10 * 2 + 128, 0 + SPRITE 35, 16, $d8, #score / 10 % 10 * 2 + 128, 0 + SPRITE 36, 16, $e0, #score % 10 * 2 + 128, 0 + SPRITE 37, 16, $e8, 128, 0 + END + +game_palette: + DATA BYTE $0F,$30,$30,$30 + DATA BYTE $0F,$30,$30,$30 + DATA BYTE $0F,$30,$30,$30 + DATA BYTE $0F,$30,$30,$30 + DATA BYTE $0F,$16,$10,$30 + DATA BYTE $0F,$12,$16,$24 + DATA BYTE $0F,$12,$10,$30 + DATA BYTE $0F,$16,$27,$30 + + CHRROM 0 + + CHRROM PATTERN 16 + ' + ' Pixel scrolling stars + ' + BITMAP ".1......" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP ".....1.." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "...1...." + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP ".1......" + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "......1." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "......1." + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP "....1..." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP ".1......" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + CHRROM PATTERN 128 + + BITMAP "3333333." + BITMAP "3.....3." + BITMAP "3.....3." + BITMAP "3....33." + BITMAP "3....33." + BITMAP "3....33." + BITMAP "3333333." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "....3..." + BITMAP "....3..." + BITMAP "....3..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP "...33..." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "3333333." + BITMAP "3.....3." + BITMAP "......3." + BITMAP "3333333." + BITMAP "33......" + BITMAP "33......" + BITMAP "3333333." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "3333333." + BITMAP "3.....3." + BITMAP "......3." + BITMAP ".333333." + BITMAP ".....33." + BITMAP "3....33." + BITMAP "3333333." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "3......." + BITMAP "3......." + BITMAP "3.....3." + BITMAP "3.....3." + BITMAP "3333333." + BITMAP ".....33." + BITMAP ".....33." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "3333333." + BITMAP "3.....3." + BITMAP "3......." + BITMAP "3333333." + BITMAP ".....33." + BITMAP "3....33." + BITMAP "3333333." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "33333..." + BITMAP "3...3..." + BITMAP "3......." + BITMAP "3333333." + BITMAP "3....33." + BITMAP "3....33." + BITMAP "3333333." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "3333333." + BITMAP "......3." + BITMAP "......3." + BITMAP ".....33." + BITMAP ".....33." + BITMAP ".....33." + BITMAP ".....33." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP ".33333.." + BITMAP ".3...3.." + BITMAP ".3...3.." + BITMAP "3333333." + BITMAP "3.....3." + BITMAP "3.....3." + BITMAP "3333333." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "3333333." + BITMAP "3.....3." + BITMAP "3.....3." + BITMAP "3333333." + BITMAP ".....33." + BITMAP ".....33." + BITMAP ".....33." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + CHRROM PATTERN 256 + + BITMAP ".......33......." + BITMAP ".......33......." + BITMAP "......3333......" + BITMAP "......3333......" + BITMAP "......3333......" + BITMAP ".....333333....." + BITMAP ".....333333....." + BITMAP ".....332233....." + BITMAP ".....322223....." + BITMAP "..332333333233.." + BITMAP ".33323333332333." + BITMAP ".33333222233333." + BITMAP "3322333332332233" + BITMAP "3332333333333233" + BITMAP "3333.333333.3333" + BITMAP ".11..11..11..11." + + BITMAP "....33333333...." + BITMAP "...3........3..." + BITMAP "..3.33....33.3.." + BITMAP ".3...333333...3." + BITMAP ".3...323323...3." + BITMAP ".3...333333...3." + BITMAP ".3....3333 ...3." + BITMAP ".3..22.33.22..3." + BITMAP ".3222.2222.2223." + BITMAP "311..211112..113" + BITMAP "..333333333333.." + BITMAP "3331111331111333" + BITMAP "3133331331333313" + BITMAP "3113313333133113" + BITMAP "3331133..3311333" + BITMAP "..3333....3333.." + + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP ".......31......." + BITMAP "......3221......" + BITMAP "......3221......" + BITMAP "......3221......" + BITMAP "......3221......" + BITMAP "....33322221...." + BITMAP "...2222222221..." + BITMAP "......2111......" + BITMAP ".....21...21...." + BITMAP "................" + BITMAP "................" + BITMAP "................" + + BITMAP "................" + BITMAP "................" + BITMAP ".......1........" + BITMAP ".......12......." + BITMAP "1......112......" + BITMAP "111.....111...11" + BITMAP "11112..1121..121" + BITMAP "...11211221112.." + BITMAP ".....11222221..." + BITMAP "......122221...." + BITMAP ".....11122211..." + BITMAP "....111.112211.." + BITMAP "...121.....1111." + BITMAP "...12........111" + BITMAP "..11............" + BITMAP "................" diff --git a/examples/space_attack_sms.bas b/examples/space_attack_sms.bas new file mode 100644 index 0000000..3bb1d8a --- /dev/null +++ b/examples/space_attack_sms.bas @@ -0,0 +1,373 @@ + ' + ' Space Attack (demo for CVBasic) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Feb/29/2024. + ' Revision date: Nov/13/2024. Added pixel stars background and double-speed enemies. + ' Revision date: Apr/26/2025. Adapted for Sega Master System. + ' + + DEFINE SPRITE 0,8,sprites_bitmaps + + DEFINE CHAR 16,8,pixel_bitmaps + + DIM enemy_x(8) + DIM enemy_y(8) + DIM enemy_s(8) + +restart_game: + CLS + MODE 4 ' In this mode, char definitions are faster + BORDER ,4 ' Avoid scrolling in the columns 24-31 + + #score = 0 + + player_x = 120 + player_y = 176 + bullet_y = 0 + FOR c = 0 TO 7 + enemy_s(c) = 0 + NEXT c + + ' Draw the stars background + ' + ' We make a vertical strip using 8 characters (numbers 16 to 23) + ' and we repeat it continuously (24 rows / 8 characters = 3 repetitions) + ' + FOR d = 0 TO 46 STEP 2 ' For each column + FOR #c = 0 TO 1728 STEP 64 + e = RANDOM(8) + 16 + VPOKE $3800 + #c + d, e ' Put strip character. + NEXT #c + NEXT d + + GOSUB update_score + +game_loop: + WAIT + + ' + ' Displace stars pixel by pixel + ' + scroll_y = scroll_y - 1 + IF scroll_y = $ff THEN scroll_y = $df + SCROLL 0, scroll_y + + ' Background "music" (two tones alternating each 16 video frames) + #c = 960 + IF FRAME AND 32 THEN #c = 1023 + d = (FRAME AND 31) / 2 + SOUND 0, #c, 15 - d + + ' Setup player sprite + SPRITE 0,player_y-1,player_x,0 + SPRITE 1,player_y-1,player_x+8,2 + + ' Setup bullet sprite + IF bullet_y = 0 THEN ' Active? + SPRITE 2,$e0,0,0 ' No, remove sprites. + SPRITE 3,$e0,0,0 + SOUND 1,,0 ' Disable sound. + ELSE + SPRITE 2,bullet_y-1,bullet_x,8 ' Setup sprites. + SPRITE 3,bullet_y-1,bullet_x+8,10 ' + bullet_y = bullet_y - 4 ' Displace bullet. + SOUND 1,bullet_y+16,11 ' Make sound. + END IF + + ' + ' Display and move the enemies. + ' + FOR c = 0 TO 7 + IF enemy_s(c) = 0 THEN ' No enemy + SPRITE 4 + c * 2, $e0, 0, 0 + SPRITE 5 + c * 2, $e0, 0, 0 + ' Create one + enemy_x(c) = RANDOM(168) + 4 + enemy_y(c) = $c0 + c * 4 + enemy_s(c) = RANDOM(2) + 1 + ELSEIF enemy_s(c) < 3 THEN ' Enemy moving. + SPRITE 4 + c * 2, enemy_y(c) - 1, enemy_x(c), 4 + SPRITE 5 + c * 2, enemy_y(c) - 1, enemy_x(c) + 8, 6 + + ' Slowly drift towards the player. + IF (FRAME AND 3) = 0 THEN + IF player_x < enemy_x(c) THEN + enemy_x(c) = enemy_x(c) - 1 + ELSE + enemy_x(c) = enemy_x(c) + 1 + END IF + END IF + ' Move down. + IF enemy_s(c) = 1 THEN + enemy_y(c) = enemy_y(c) + 2 + ELSE + enemy_y(c) = enemy_y(c) + 3 + END IF + IF enemy_y(c) >= $c0 AND enemy_y(c) <= $c7 THEN ' Reached bottom. + enemy_x(c) = RANDOM(168) + 4 + enemy_y(c) = $f2 ' Reset enemy. + enemy_s(c) = RANDOM(2) + 1 + END IF + + ' + ' Check if bullet has been launched. + ' + IF bullet_y <> 0 THEN ' Is bullet launched? + IF ABS(bullet_x + 1 - enemy_x(c)) < 8 THEN + IF ABS(bullet_y + 1 - enemy_y(c)) < 8 THEN + enemy_s(c) = 3 ' Enemy explodes + #score = #score + 1 + GOSUB update_score + bullet_y = 0 + sound 2,2 ' Start enemy explosion sound + SOUND 3,$E7,13 + END IF + END IF + END IF + + ' + ' Check if player is hit by enemy. + ' + IF ABS(player_y + 1 - enemy_y(c)) < 8 THEN + IF ABS(player_x + 1 - enemy_x(c)) < 8 THEN + GOTO player_dies + END IF + END IF + ELSE + ' Enemy explosion. + IF FRAME AND 4 THEN + SPRITE 4 + c * 2, $e0, 0, 0 + SPRITE 5 + c * 2, $e0, 0, 0 + ELSE + SPRITE 4 + c * 2, enemy_y(c) - 1, enemy_x(c), 12 + SPRITE 5 + c * 2, enemy_y(c) - 1, enemy_x(c) + 8, 14 + END IF + + ' Displace explosion slowly. + IF FRAME AND 1 THEN + IF enemy_y(c) < $c0 THEN enemy_y(c) = enemy_y(c) + 1 + END IF + + ' Explosion sound. + SOUND 2,enemy_s(c) + enemy_s(c) = enemy_s(c) + 1 + IF enemy_s(c) = 80 THEN ' Time reached. + SOUND 3,,0 + enemy_x(c) = RANDOM(168) + 4 + enemy_y(c) = $f2 + enemy_s(c) = 1 ' Bring back enemy. + END IF + END IF + NEXT c + + ' + ' Movement of the player. + ' + IF cont1.left THEN IF player_x > 0 THEN player_x = player_x - 2 + IF cont1.right THEN IF player_x < 176 THEN player_x = player_x + 2 + IF cont1.button THEN ' Fire! + IF bullet_y = 0 THEN ' Only if no bullet active. + bullet_y = player_y - 8 + bullet_x = player_x + END IF + END IF + GOTO game_loop + + ' + ' Player dies. + ' +player_dies: + PRINT AT 250,"GAME" + PRINT AT 282,"OVER" + + ' + ' Explosion effect and sound. + ' + SOUND 0,,0 + SOUND 1,,0 + SOUND 2,32,0 + FOR c = 0 TO 120 + WAIT + SOUND 3,$E4 + (c AND 3),13 + e = player_y - 1 + RANDOM(5) - 2 + d = player_x + RANDOM(5) - 2 + SPRITE 0, e, d, 12 + SPRITE 1, e, d + 8, 14 + NEXT c + SOUND 3,,0 + + ' + ' Remove enemies. + ' + FOR c = 2 TO 19 + SPRITE c, $e0, 0, 0 + NEXT c + + ' + ' Big delay. + ' + #c = FRAME + DO + WAIT + LOOP WHILE FRAME - #c < 300 + + GOTO restart_game + + ' + ' Update the score on the screen. + ' +update_score: PROCEDURE + PRINT AT 57,"SCORE:" + PRINT AT 89,#score,"0" + END + + ' + ' Pixel scrolling stars + ' +pixel_bitmaps: + BITMAP ".5......" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP ".....5.." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "...5...." + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP ".5......" + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "......5." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "......5." + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP "....5..." + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP ".5......" + BITMAP "........" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + ' + ' Bitmaps for the game. + ' +sprites_bitmaps: + BITMAP ".......FF......." + BITMAP ".......FF......." + BITMAP "......FFFF......" + BITMAP "......FFFF......" + BITMAP "......FFFF......" + BITMAP ".....FFFFFF....." + BITMAP ".....FFFFFF....." + BITMAP ".....FF55FF....." + BITMAP ".....F5555F....." + BITMAP "..FF4FFFFFF4FF.." + BITMAP ".FFF4FFFFFF4FFF." + BITMAP ".FFFFF4444FFFFF." + BITMAP "FF44FFFFF4FF44FF" + BITMAP "FFF4FFFFFFFFF4FF" + BITMAP "FFFF.FFFFFF.FFFF" + BITMAP ".66..66..66..66." + + BITMAP "....33333333...." + BITMAP "...3........3..." + BITMAP "..3.FF....FF.3.." + BITMAP ".3...FFFFFF...3." + BITMAP ".3...FDFFDF...3." + BITMAP ".3...FFFFFF...3." + BITMAP ".3....FFFF ...3." + BITMAP ".3..DD.FF.DD..3." + BITMAP ".3DDD.DDDD.DDD3." + BITMAP "366..D5555D..663" + BITMAP "..333333333333.." + BITMAP "333CCCC33CCCC333" + BITMAP "3C3333C33C3333C3" + BITMAP "3CC33C3333C33CC3" + BITMAP "333CC33..33CC333" + BITMAP "..3333....3333.." + + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP "................" + BITMAP ".......F5......." + BITMAP "......F775......" + BITMAP "......F775......" + BITMAP "......F775......" + BITMAP "......F775......" + BITMAP "....FFF77775...." + BITMAP "...7777777775..." + BITMAP "......7555......" + BITMAP ".....75...75...." + BITMAP "................" + BITMAP "................" + BITMAP "................" + + BITMAP "................" + BITMAP "................" + BITMAP ".......6........" + BITMAP ".......6A......." + BITMAP "6......66A......" + BITMAP "666.....666...66" + BITMAP "6666A..66A6..6A6" + BITMAP "...66A66AA666A.." + BITMAP ".....66AAAAA6..." + BITMAP "......6AAAA6...." + BITMAP ".....666AAA66..." + BITMAP "....666.66AA66.." + BITMAP "...6A6.....6666." + BITMAP "...6A........666" + BITMAP "..66............" + BITMAP "................" diff --git a/examples/spinner.bas b/examples/spinner.bas new file mode 100644 index 0000000..2a080ab --- /dev/null +++ b/examples/spinner.bas @@ -0,0 +1,67 @@ + ' + ' Spinner / Roller Controller test (Colecovision) + ' Demo for CVBasic. + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Feb/05/2026. + ' + + DEFINE SPRITE 0,1,happy_face + + PRINT AT 36,"Happy face!" + + PRINT AT 98,"CONT1.SPINNER max.per frame" + + PRINT AT 162,"CONT2.SPINNER max.per frame" + + x = 120 + y = 88 + + max_x = 0 + max_y = 0 + + counter = 0 + +game_loop: + WAIT + IF counter = 60 THEN + counter = 0 + PRINT AT 130,<3>max_x + PRINT AT 194,<3>max_y + END IF + + counter = counter + 1 + + #dx = CONT1.SPINNER + #dy = CONT2.SPINNER + mx = ABS(#dx) + my = ABS(#dy) + IF mx > max_x THEN max_x = mx + IF my > max_y THEN max_y = my + + SPRITE 0, y - 1, x, 0, 10 + + x = x + #dx + y = y - #dy + + GOTO game_loop + +happy_face: + BITMAP "......XXXX......" + BITMAP "....XXXXXXXX...." + BITMAP "...XXXXXXXXXX..." + BITMAP "..XXXXXXXXXXXX.." + BITMAP ".XXXXXXXXXXXXXX." + BITMAP ".XXXXXXXXXXXXXX." + BITMAP "XXXX..XXXX..XXXX" + BITMAP "XXXX..XXXX..XXXX" + BITMAP "XXXXXXXXXXXXXXXX" + BITMAP "XXXXXXXXXXXXXXXX" + BITMAP ".XXXXXXXXXXXXXX." + BITMAP ".XXX..XXXX..XXX." + BITMAP "..XXX......XXX.." + BITMAP "...XXX....XXX..." + BITMAP "....XXXXXXXX...." + BITMAP "......XXXX......" diff --git a/examples/strings.bas b/examples/strings.bas new file mode 100644 index 0000000..b2788b0 --- /dev/null +++ b/examples/strings.bas @@ -0,0 +1,74 @@ + ' + ' String handling in CVBASIC + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Apr/14/2025. + ' + + ' + ' CVBasic doesn't support directly strings, but enough + ' support to provide the data and do processing. + ' + + ' This is a macro, CVBasic doesn't process strings + DEF FN center_string(pos, str) = PRINT AT pos - LEN(str)/2, str + + DEF FN wait_for_button(a) = FOR c = 0 TO 15:WAIT:NEXT:DO:WAIT:LOOP WHILE cont.button = 0 + + DEF FN get_string(where, num) = RESTORE where: c = num: WHILE c > 0: DO: READ BYTE char: LOOP WHILE char <> 0: c = c - 1: WEND + + DEF FN print_string(a) = WHILE 1: READ BYTE char: IF char <> 0 THEN PRINT CHR$(char): WEND + + WHILE 1 + + CLS + + center_string(80, "Hello, World!") + + wait_for_button(0) + + CLS + + get_string(messages_1, RANDOM(10)) + + print_string(0) + + get_string(messages_2, RANDOM(5)) + + print_string(0) + + get_string(messages_3, RANDOM(5)) + + print_string(0) + + wait_for_button(0) + + WEND + +messages_1: + DATA BYTE "William",0 + DATA BYTE "Olivia",0 + DATA BYTE "Jack",0 + DATA BYTE "Amelia",0 + DATA BYTE "Michael",0 + DATA BYTE "Eleanor",0 + DATA BYTE "Noah",0 + DATA BYTE "Lily",0 + DATA BYTE "James",0 + DATA BYTE "Emma",0 + +messages_2: + DATA BYTE " likes ",0 + DATA BYTE " dislikes ",0 + DATA BYTE " loves ",0 + DATA BYTE " hates ",0 + DATA BYTE " enjoys ",0 + +messages_3: + DATA BYTE "frogs",0 + DATA BYTE "horses",0 + DATA BYTE "dogs",0 + DATA BYTE "cats",0 + DATA BYTE "turtles",0 diff --git a/examples/test1.bas b/examples/test1.bas index 1da0cdc..b00bd28 100644 --- a/examples/test1.bas +++ b/examples/test1.bas @@ -5,6 +5,7 @@ ' https://nanochess.org/ ' ' Creation date: Feb/29/2024. + ' Revision date: Apr/27/2025. Uses new CHR$ syntax. ' DIM #stars(24) @@ -20,10 +21,10 @@ WAIT FOR c = 0 TO 23 - VPOKE $1800 + #stars(c), 32 + PRINT AT #stars(c), CHR$(32) #stars(c) = #stars(c) + 32 IF #stars(c) >= 768 THEN #stars(c) = #stars(c) - 736 - VPOKE $1800 + #stars(c), 42 + PRINT AT #stars(c), CHR$(42) NEXT c WEND diff --git a/examples/test2.bas b/examples/test2.bas index ac8ad31..7e167cd 100644 --- a/examples/test2.bas +++ b/examples/test2.bas @@ -105,7 +105,7 @@ READ BYTE c IF c = 0 THEN EXIT DO - VPOKE #pointer, c + PRINT AT #pointer, CHR$(c) #pointer = #pointer + 1 LOOP WHILE 1 diff --git a/examples/test3_sms.bas b/examples/test3_sms.bas new file mode 100644 index 0000000..bb801f6 --- /dev/null +++ b/examples/test3_sms.bas @@ -0,0 +1,73 @@ + ' + ' Test 3 + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Nov/12/2024. + ' Revision date: Apr/27/2025. Adapted for Sega Master System. + ' + + DEFINE SPRITE 0, 2, sprite_bitmap + + sprite_x = 148 + sprite_y = 116 + + #state = 0 + d = 1 + + WHILE 1 + + SPRITE 0, sprite_y - 1, sprite_x, 0 + SPRITE 1, sprite_y - 1, sprite_x + 8, 2 + + WAIT + WAIT + + d = d - 1 + IF d = 0 THEN + d = 40 + #state = #state + 1 + IF #state = 10 THEN #state = 1 + END IF + + SELECT CASE #state + CASE 1 + PALETTE 10 + 16, $0F + sprite_x = sprite_x - 2 + CASE 2 + sprite_y = sprite_y - 2 + CASE 3 + sprite_x = sprite_x + 2 + CASE 4 + sprite_y = sprite_y + 2 + CASE 5 TO 6 + sprite_x = sprite_x - 1 + sprite_y = sprite_y - 1 + CASE 7 TO 8 + sprite_x = sprite_x + 1 + sprite_y = sprite_y + 1 + CASE ELSE + PALETTE 10 + 16, RANDOM(63) + 1 + END SELECT + + WEND + + +sprite_bitmap: + BITMAP "......AAAA......" + BITMAP "......AAAA......" + BITMAP "......AAAA......" + BITMAP "......AAAA......" + BITMAP ".......AA......." + BITMAP "..A....AA......." + BITMAP ".AAAAAAAAAAA...." + BITMAP ".AAAAAAAAAAAA..." + BITMAP ".......AA...AA.." + BITMAP ".......AA....AA." + BITMAP "......AAAA....AA" + BITMAP ".....AA..AA....." + BITMAP ".....AA..AA....." + BITMAP ".....AA..AA....." + BITMAP ".....AA..AA....." + BITMAP "....AAA..AAA...." diff --git a/examples/test4.bas b/examples/test4.bas new file mode 100644 index 0000000..239e31f --- /dev/null +++ b/examples/test4.bas @@ -0,0 +1,85 @@ + ' + ' Benchmark and multiplication/division routines test. + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Mar/25/2026. + ' + + #time = FRAME + FOR #c = 1 TO 65534 + #d = $0001 * #c + IF FRAME - #time >= 60 THEN EXIT FOR + NEXT #c + PRINT AT $0002,<.5>#c," muls $0001 in 1 second" + #time = FRAME + FOR #c = 1 TO 65534 + #d = $00ff * #c + IF FRAME - #time >= 60 THEN EXIT FOR + NEXT #c + PRINT AT $0022,<.5>#c," muls $00ff in 1 second" + #time = FRAME + FOR #c = 1 TO 65534 + #d = $8000 * #c + IF FRAME - #time >= 60 THEN EXIT FOR + NEXT #c + PRINT AT $0042,<.5>#c," muls $8000 in 1 second" + #time = FRAME + FOR #c = 1 TO 65534 + #d = $aa55 * #c + IF FRAME - #time >= 60 THEN EXIT FOR + NEXT #c + PRINT AT $0062,<.5>#c," muls $aa55 in 1 second" + + #time = FRAME + FOR #c = 1 TO 65534 + #d = $00ff / #c + IF FRAME - #time >= 60 THEN EXIT FOR + NEXT #c + PRINT AT $00C2,#c," divs $00ff in 1 second." + + #time = FRAME + FOR #c = 1 TO 65534 + #d = $FFFF / #c + IF FRAME - #time >= 60 THEN EXIT FOR + NEXT #c + PRINT AT $00e2,#c," divs $ffff in 1 second." + + #e = $0101 + FOR #c = 1 TO 65000 STEP 101 + #d = $0101 * #c + PRINT AT $0122,"$0101 * ",#c, " = ",<.5>#d + IF #d <> #e THEN PRINT AT $0142,"bad ",#c + #e = #e + $0101 * 101 + NEXT #c + + #e = $0100 + FOR #c = 1 TO 65000 STEP 101 + #d = $0100 * #c + PRINT AT $0162,"$0100 * ",#c, " = ",<.5>#d + IF #d <> #e THEN PRINT AT $0182,"bad ",#c + #e = #e + $0100 * 101 + NEXT #c + + #e = $0001 + FOR #c = 1 TO 65000 STEP 101 + #d = $0001 * #c + PRINT AT $01A2,"$0001 * ",#c, " = ",<.5>#d + IF #d <> #e THEN PRINT AT $01C2,"bad ",#c + #e = #e + $0001 * 101 + NEXT #c + + #e = $0007 + FOR #c = 1 TO 1000 + #d = #e / 7 + PRINT AT $01e2,#e, " / $0007 = ",<.5>#d + IF #d <> #c THEN PRINT AT $0202,"bad ",#c + #e = #e + 7 + NEXT #c + + WHILE 1: WEND + + ' For testing NES code generation. +' CHRROM 0 + diff --git a/examples/test5.bas b/examples/test5.bas new file mode 100644 index 0000000..6b929e6 --- /dev/null +++ b/examples/test5.bas @@ -0,0 +1,28 @@ + ' + ' C-style preprocessor test + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Jul/23/2026. + ' + + ' You can use the platform constant (equivalent to the compilation option), + ' or a constant provided via command-line, or a constant defined inside + ' the program. + +#if COLECOVISION + PRINT AT 33,"Compiled for Colecovision" +#endif + +#if SGM + PRINT AT 65,"Compiled for SGM" +#endif + +#if COLECOVISION + PRINT AT 97,"This is a Colecovision" +#else + PRINT AT 97,"This isn't a Colecovision" +#endif + + WHILE 1: WEND diff --git a/examples/varptr_sms.bas b/examples/varptr_sms.bas new file mode 100644 index 0000000..f83f415 --- /dev/null +++ b/examples/varptr_sms.bas @@ -0,0 +1,70 @@ + ' + ' Using VARPTR to redefine graphics for levels + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Aug/26/2024. + ' Revision date: Sep/27/2025. Adapted to Sega Master System. + ' + + level = 1 + + WHILE 1 + + CLS + + DEFINE CHAR 128,2,VARPTR level_bitmaps((level - 1) * 64) ' Each character is 32 bytes. 2*32 = 64 + + FOR c = 1 TO 10 + PRINT AT RANDOM(768),"\128" + NEXT c + FOR c = 1 TO 10 + PRINT AT RANDOM(768),"\129" + NEXT c + + FOR c = 1 TO 120: WAIT: NEXT + + level = level + 1 + + IF level = 3 THEN level = 1 + + WEND + +level_bitmaps: + BITMAP "..aaaa.." + BITMAP ".a....a." + BITMAP "a.a..a.a" + BITMAP "a......a" + BITMAP "a.a..a.a" + BITMAP "a..aa..a" + BITMAP ".a....a." + BITMAP "..aaaa.." + + BITMAP ".....3.." + BITMAP "....3333" + BITMAP "33..3.33" + BITMAP ".3333333" + BITMAP "..3333.." + BITMAP ".33333.." + BITMAP ".33..33." + BITMAP ".33..33." + + BITMAP "..9999.." + BITMAP ".9....9." + BITMAP "9......9" + BITMAP "9.9..9.9" + BITMAP "9......9" + BITMAP "9......9" + BITMAP ".9....9." + BITMAP "..9999.." + + BITMAP "..5..5.." + BITMAP ".55..55." + BITMAP ".555555." + BITMAP ".5.55.5." + BITMAP ".55555.5" + BITMAP "..5555.5" + BITMAP ".55.5555" + BITMAP ".55.5555" + diff --git a/examples/vgm.bas b/examples/vgm.bas index 466e8ae..7c6aace 100644 --- a/examples/vgm.bas +++ b/examples/vgm.bas @@ -12,7 +12,7 @@ ' ' I/O port where the PSG is located in hardware. ' -' CONST VGM_PSG_PORT = $7F ' For SG-1000 +' CONST VGM_PSG_PORT = $7F ' For SG-1000/SMS CONST VGM_PSG_PORT = $FF ' For Colecovision ON FRAME GOSUB vgm_play diff --git a/examples/vgm_ay3.bas b/examples/vgm_ay3.bas new file mode 100644 index 0000000..f8d1b99 --- /dev/null +++ b/examples/vgm_ay3.bas @@ -0,0 +1,88 @@ + ' + ' Mini VGM player for CVBasic + ' + ' This handles only AY-3-8910 VGM files (feed with 3.58 mhz) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: May/06/2025. + ' + + ' + ' I/O port where the PSG is located in hardware. + ' +' CONST VGM_PSG_PORT = $A0 ' For MSX + CONST VGM_PSG_PORT = $50 ' For Colecovision Super Game Module + + ON FRAME GOSUB vgm_play + + PRINT AT 33,"VGM playing..." + + ' + ' Start playing a VGM song. + ' + #vgm_song = VARPTR vgm_music(0) + GOSUB vgm_start + + ' + ' Does nothing except to show the current playing pointer. + ' + WHILE 1 + WAIT + PRINT AT 65,#vgm_pointer," " + WEND + + ' + ' Start playing a VGM song. + ' Input: #vgm_song = pointer to song. + ' +vgm_start: PROCEDURE + #vgm_end = #vgm_song + (PEEK(#vgm_song + 4) + (PEEK(#vgm_song + 5) * 256)) + IF PEEK(#vgm_song + 8) < $50 THEN + #vgm_pointer = #vgm_song + 64 + ELSE + #vgm_pointer = #vgm_song + $34 + (PEEK(#vgm_song + 52) + (PEEK(#vgm_song + 53) * 256)) + END IF + + END + + ' + ' This routine is called on each video frame to play the next audio data. + ' +vgm_play: PROCEDURE + IF #vgm_pointer = 0 THEN RETURN + + IF #vgm_pointer >= #vgm_end THEN #vgm_pointer = 0: RETURN + + WHILE 1 + vgm_byte = PEEK(#vgm_pointer) + #vgm_pointer = #vgm_pointer + 1 + IF vgm_byte = $a0 THEN + OUT VGM_PSG_PORT,PEEK(#vgm_pointer) + #vgm_pointer = #vgm_pointer + 1 + OUT VGM_PSG_PORT + 1,PEEK(#vgm_pointer) + #vgm_pointer = #vgm_pointer + 1 + ELSEIF vgm_byte = $61 THEN + #vgm_pointer = #vgm_pointer + 2 + EXIT WHILE + ELSEIF vgm_byte = $62 THEN + EXIT WHILE + ELSEIF vgm_byte = $63 THEN + EXIT WHILE + ELSEIF vgm_byte = $66 THEN + #vgm_pointer = 0 + EXIT WHILE + END IF + WEND + END + + ' + ' You can download VGM music from: + ' https://vgmrips.net/packs/chip/ay-3-8910?letter=L + ' + ' You need to decompress the file, and inside each file is compressed with gzip, + ' so you need to decompress again each file. + ' +vgm_music: + ASM INCBIN "02 map screen.vgm" diff --git a/examples/vgm_nes.bas b/examples/vgm_nes.bas new file mode 100644 index 0000000..2d3479b --- /dev/null +++ b/examples/vgm_nes.bas @@ -0,0 +1,92 @@ + ' + ' Mini VGM player for CVBasic + ' + ' This handles only NES APU VGM files + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Jun/20/2026. + ' + + ON FRAME GOSUB vgm_play + + PRINT AT 33,"VGM playing..." + + ' + ' Start playing a VGM song. + ' + #vgm_song = VARPTR vgm_music(0) + GOSUB vgm_start + + ' + ' Does nothing except to show the current playing pointer. + ' + WHILE 1 + WAIT + PRINT AT 65,#vgm_pointer," " + WEND + + ' + ' Start playing a VGM song. + ' Input: #vgm_song = pointer to song. + ' +vgm_start: PROCEDURE + #vgm_end = #vgm_song + (PEEK(#vgm_song + 4) + (PEEK(#vgm_song + 5) * 256)) + IF PEEK(#vgm_song + 8) < $50 THEN + #vgm_pointer = #vgm_song + 64 + ELSE + #vgm_pointer = #vgm_song + $34 + (PEEK(#vgm_song + 52) + (PEEK(#vgm_song + 53) * 256)) + END IF + + END + + ' + ' This routine is called on each video frame to play the next audio data. + ' +vgm_play: PROCEDURE + IF #vgm_pointer = 0 THEN RETURN + + IF #vgm_pointer >= #vgm_end THEN #vgm_pointer = 0: RETURN + + WHILE 1 + vgm_byte = PEEK(#vgm_pointer) + #vgm_pointer = #vgm_pointer + 1 + IF vgm_byte = $b4 THEN + vgm_reg = PEEK(#vgm_pointer) + #vgm_pointer = #vgm_pointer + 1 + IF vgm_reg >= $20 AND vgm_reg <= $3E THEN vgm_reg = vgm_reg + $60 + IF vgm_reg = $3f THEN vgm_reg = $23 + POKE $4000 + vgm_reg, PEEK(#vgm_pointer) + #vgm_pointer = #vgm_pointer + 1 + ' ELSEIF vgm_byte = $a0 THEN + ' #vgm_pointer = #vgm_pointer + 2 ' Ignore YM2149 (AY-3-8910) + ' ELSEIF vgm_byte = $51 THEN + ' #vgm_pointer = #vgm_pointer + 2 ' Ignore YM2413 (VRC7) + ELSEIF vgm_byte = $61 THEN + #vgm_pointer = #vgm_pointer + 2 + EXIT WHILE + ELSEIF vgm_byte = $62 THEN + EXIT WHILE + ELSEIF vgm_byte = $63 THEN + EXIT WHILE + ELSEIF vgm_byte = $66 THEN + #vgm_pointer = 0 + POKE $4000,0 + POKE $4004,0 + POKE $4008,0 + POKE $400C,0 + EXIT WHILE + END IF + WEND + END + + ' + ' You can download VGM music from: + ' https://vgmrips.net/packs/chip/nes-apu + ' + ' You need to decompress the file, and inside each file is compressed with gzip, + ' so you need to decompress again each file. + ' +vgm_music: + ASM INCBIN "stage.vgm" diff --git a/examples/viboritas.bas b/examples/viboritas.bas index 0e0c1bf..4bd712d 100644 --- a/examples/viboritas.bas +++ b/examples/viboritas.bas @@ -21,6 +21,7 @@ restart_game: lives = 2 level = 1 restart_level: + WAIT PRINT AT 684,"Lives: ",lives PRINT AT 745,"nanochess 1990" diff --git a/examples/viboritas_msx2.bas b/examples/viboritas_msx2.bas new file mode 100644 index 0000000..b37e591 --- /dev/null +++ b/examples/viboritas_msx2.bas @@ -0,0 +1,925 @@ + ' + ' Viboritas (demo for CVBasic) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Oct/1990. + ' Revision date: Feb/29/2024. Ported to CVBasic. + ' Revision date: Feb/10/2026. Adapted to MSX2. + ' + + ' The original game was made in Z80 assembler, + ' you can see it here: https://nanochess.org/viboritas.html + + ' It is easier to understand in CVBasic ;) + + MODE 4 + SCREEN DISABLE + PALETTE LOAD game_palette + FOR c = 32 TO 127 + DEFINE COLOR c,1,letter_colors + NEXT c + SCREEN ENABLE + DEFINE CHAR 128,21,game_bitmaps + DEFINE COLOR 128,21,game_colors + DEFINE SPRITE 0,20,game_sprites + DEFINE SPRITE COLOR 0,2,VARPTR sprites_color(0) + DEFINE SPRITE COLOR 2,2,VARPTR sprites_color(32) + DEFINE SPRITE COLOR 4,2,VARPTR sprites_color(32) + DEFINE SPRITE COLOR 6,2,VARPTR sprites_color(32) + +restart_game: + lives = 2 + level = 1 +restart_level: + WAIT + + PRINT AT 684,"Lives: ",lives + PRINT AT 745,"nanochess 1990" + +next_level: + GOSUB draw_level + + x_player = 8 + y_player = 16 + player_frame = 0 + + x_enemy1 = random(128) + 64 + y_enemy1 = 56 + enemy1_frame = 24 + x_enemy2 = random(128) + 80 + y_enemy2 = 96 + enemy2_frame = 32 + x_enemy3 = random(128) + 48 + y_enemy3 = 136 + enemy3_frame = 24 + + enemy_speed = 0 + + GOSUB start_song + +game_loop: + WHILE 1 + WAIT + GOSUB play_song + + SPRITE 0, y_player - 1, x_player, player_frame * 2 + SPRITE 1, y_player - 1, x_player, player_frame * 2 + 4 + SPRITE 2, y_enemy1 - 1, x_enemy1, enemy1_frame * 2 + SPRITE 3, y_enemy1 - 1, x_enemy1, enemy1_frame * 2 + 4 + SPRITE 4, y_enemy2 - 1, x_enemy2, enemy2_frame * 2 + SPRITE 5, y_enemy2 - 1, x_enemy2, enemy2_frame * 2 + 4 + SPRITE 6, y_enemy3 - 1, x_enemy3, enemy3_frame * 2 + SPRITE 7, y_enemy3 - 1, x_enemy3, enemy3_frame * 2 + 4 + + GOSUB move_player + + c = $50 + level * 4 + enemy_speed = enemy_speed + c + WHILE enemy_speed >= $40 + enemy_speed = enemy_speed - $40 + GOSUB move_enemies + WEND + IF cont1.button THEN + IF x_player > 232 AND x_player < 248 AND y_player = 136 THEN + GOSUB sound_off + + FOR c = 1 to 10 + WAIT + SOUND 5, 200 - c * 10, 13 + NEXT c + + level = level + 1 + IF level = 6 THEN + GOSUB sound_off + PRINT AT 267," YOU WIN! " + #c = FRAME + DO + WAIT + LOOP WHILE FRAME - #c < 300 + level = 1 + GOTO restart_level + END IF + GOTO next_level + END IF + END IF + IF ABS(y_player + 1 - y_enemy1) < 8 THEN + IF ABS(x_player + 1 - x_enemy1) < 8 THEN GOTO player_dies + END IF + IF ABS(y_player + 1 - y_enemy2) < 8 THEN + IF ABS(x_player + 1 - x_enemy2) < 8 THEN GOTO player_dies + END IF + IF ABS(y_player + 1 - y_enemy3) < 8 THEN + IF ABS(x_player + 1 - x_enemy3) < 8 THEN GOTO player_dies + END IF + WEND + +player_dies: + GOSUB sound_off + + SOUND 5,640,13 + SOUND 6,320,13 + SOUND 7,160,13 + + player_frame = 0 + FOR c = 0 TO 30 + WAIT + WAIT + player_frame = player_frame XOR 8 + SPRITE 0, y_player - 1, x_player, player_frame * 2 + SPRITE 1, y_player - 1, x_player, player_frame * 2 + 4 + NEXT c + + GOSUB sound_off + + DO + WAIT + SOUND 5,200 - y_player,13 + player_frame = player_frame XOR 8 + SPRITE 0, y_player - 1, x_player, player_frame * 2 + SPRITE 1, y_player - 1, x_player, player_frame * 2 + 4 + y_player = y_player + 2 + LOOP WHILE y_player < 160 + + GOSUB sound_off + + IF lives = 0 THEN + PRINT AT 267," GAME OVER " + #c = FRAME + DO + WAIT + LOOP WHILE FRAME - #c < 300 + GOTO restart_game + END IF + lives = lives - 1 + GOTO restart_level + + ' + ' Draw the current level. + ' +draw_level: PROCEDURE + + ' Get the base character to draw the level. + base_character = 128 + (level - 1) * 4 + + ' Draw the background. + FOR #c = $1800 TO $1a7c STEP 4 + VPOKE #c, base_character + VPOKE #c + 1, base_character + VPOKE #c + 2, base_character + VPOKE #c + 3, base_character + 1. + NEXT #c + + ' Draw over the floors. + FOR #c = $1880 TO $1A60 STEP 160 + FOR #d = #c TO #c + 31 + VPOKE #d, base_character + 2. + NEXT #d + NEXT #c + + ' Draw the ladders. + ladders = 6 - level + + FOR #c = $1880 TO $19C0 STEP 160 + FOR d = 1 TO ladders + e = RANDOM(28) + 2 + VPOKE #c + e, base_character + 3. + VPOKE #c + e + 32, base_character + 3. + VPOKE #c + e + 64, base_character + 3. + VPOKE #c + e + 96, base_character + 3. + VPOKE #c + e + 128, base_character + 3. + NEXT d + NEXT #c + + ' Draw the "exit". + VPOKE $1A5E, 148 + + END + + ' + ' Move the player + ' +move_player: PROCEDURE + IF cont1.left THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor + IF x_player > 0 THEN x_player = x_player - 1 + IF FRAME AND 4 THEN player_frame = 8 ELSE player_frame = 12 + END IF + END IF + IF cont1.right THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor + IF x_player < 240 THEN x_player = x_player + 1 + IF FRAME AND 4 THEN player_frame = 0 ELSE player_frame = 4 + END IF + END IF + IF cont1.up THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor. + column = (x_player + 7) /8 + row = (y_player + 8) / 8 + #c = $1800 + row * 32 + column + IF VPEEK(#c) = base_character + 3 THEN ' Ladder? + y_player = y_player - 1 + END IF + ELSE + IF FRAME AND 4 THEN player_frame = 16 ELSE player_frame = 20 + y_player = y_player - 1 + END IF + END IF + IF cont1.down THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor. + column = (x_player + 7) /8 + row = (y_player + 16) / 8 + #c = $1800 + row * 32 + column + IF VPEEK(#c) = base_character + 3 THEN ' Ladder? + y_player = y_player + 1 + END IF + ELSE + IF FRAME AND 4 THEN player_frame = 16 ELSE player_frame = 20 + y_player = y_player + 1 + END IF + END IF + END + + ' + ' Move the enemies. + ' +move_enemies: PROCEDURE + IF enemy1_frame < 32 THEN + x_enemy1 = x_enemy1 - 1. + IF x_enemy1 = 0 THEN enemy1_frame = 32 + ELSE + x_enemy1 = x_enemy1 + 1. + IF x_enemy1 = 240 THEN enemy1_frame = 24 + END IF + enemy1_frame = (enemy1_frame AND $f8) + (FRAME AND 4) + + IF enemy2_frame < 32 THEN + x_enemy2 = x_enemy2 - 1. + IF x_enemy2 = 0 THEN enemy2_frame = 32 + ELSE + x_enemy2 = x_enemy2 + 1. + IF x_enemy2 = 240 THEN enemy2_frame = 24 + END IF + enemy2_frame = (enemy2_frame AND $f8) + (FRAME AND 4) + + IF enemy3_frame < 32 THEN + x_enemy3 = x_enemy3 - 1. + IF x_enemy3 = 0 THEN enemy3_frame = 32 + ELSE + x_enemy3 = x_enemy3 + 1. + IF x_enemy3 = 240 THEN enemy3_frame = 24 + END IF + enemy3_frame = (enemy3_frame AND $f8) + (FRAME AND 4) + END + +game_bitmaps: + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + + BITMAP "XXX..XXX" + BITMAP "XXX..XXX" + BITMAP "XXX..XXX" + BITMAP "XXX..XXX" + BITMAP "XXX..XXX" + BITMAP "XXX..XXX" + BITMAP "XXX..XXX" + BITMAP "XXX..XXX" + + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + BITMAP "........" + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + BITMAP "........" + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + + BITMAP ".X....X." + BITMAP ".X....X." + BITMAP ".XXXXXX." + BITMAP ".X....X." + BITMAP ".X....X." + BITMAP ".XXXXXX." + BITMAP ".X....X." + BITMAP ".X....X." + + BITMAP "XXXXXXX." + BITMAP "X.....X." + BITMAP "X.XXX.X." + BITMAP "X.X.X.X." + BITMAP "X.XXX.X." + BITMAP "X.....X." + BITMAP "XXXXXXX." + BITMAP "........" + + BITMAP "X.XXX.X." + BITMAP "X.XXX.X." + BITMAP "X.XXX.X." + BITMAP "X.XXX.X." + BITMAP "X.XXX.X." + BITMAP "X.XXX.X." + BITMAP "X.XXX.X." + BITMAP "X.XXX.X." + + BITMAP "XXX.XXX." + BITMAP "........" + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP ".X....X." + BITMAP ".X....X." + BITMAP ".XXXXXX." + BITMAP ".X....X." + BITMAP ".X....X." + BITMAP ".XXXXXX." + BITMAP ".X....X." + BITMAP ".X....X." + + BITMAP "XXX.XXXX" + BITMAP "XXX.XXXX" + BITMAP "XXX.XXXX" + BITMAP "........" + BITMAP "XXXXXXX." + BITMAP "XXXXXXX." + BITMAP "XXXXXXX." + BITMAP "........" + + BITMAP ".XXXXXX." + BITMAP ".XXXXXX." + BITMAP ".XXXXXX." + BITMAP "........" + BITMAP ".XX.XXX." + BITMAP ".XX.XXX." + BITMAP ".XX.XXX." + BITMAP "........" + + BITMAP "........" + BITMAP "XXXXXXXX" + BITMAP "XXXXXXXX" + BITMAP "X.X.X.X." + BITMAP ".X...X.." + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP ".X....X." + BITMAP ".X....X." + BITMAP ".XXXXXX." + BITMAP ".X....X." + BITMAP ".X....X." + BITMAP ".XXXXXX." + BITMAP ".X....X." + BITMAP ".X....X." + + BITMAP "XXX.XXX." + BITMAP "XXX.XXX." + BITMAP "XXX.XXX." + BITMAP "........" + BITMAP "XXX.XXX." + BITMAP "XXX.XXX." + BITMAP "XXX.XXX." + BITMAP "........" + + BITMAP ".X......" + BITMAP "..XX...." + BITMAP "....XX.." + BITMAP "......XX" + BITMAP "....XX.." + BITMAP "..XX...." + BITMAP ".X......" + BITMAP ".X......" + + BITMAP "........" + BITMAP "XXXXXXXX" + BITMAP "........" + BITMAP "X.X.X.X." + BITMAP ".X.X.X.X" + BITMAP "........" + BITMAP "XXXXXXXX" + BITMAP "........" + + BITMAP "X......X" + BITMAP "X......X" + BITMAP "XX....XX" + BITMAP "X.XXXX.X" + BITMAP "X......X" + BITMAP "X......X" + BITMAP "XX....XX" + BITMAP "X.XXXX.X" + + BITMAP "X......X" + BITMAP ".X.XX..." + BITMAP "..XX.XXX" + BITMAP ".X...XXX" + BITMAP "..XXX..X" + BITMAP "..X..XXX" + BITMAP ".X..X..X" + BITMAP "..X..XXX" + + BITMAP ".X...XXX" + BITMAP ".X..X..X" + BITMAP "..X..XXX" + BITMAP ".X......" + BITMAP "..X.X..." + BITMAP "...X.X.X" + BITMAP "...X..X." + BITMAP "..X..XXX" + + BITMAP "........" + BITMAP "XXXXXXX." + BITMAP "XXXXXXX." + BITMAP "........" + BITMAP "XXX.XXXX" + BITMAP "XXX.XXXX" + BITMAP "........" + BITMAP "........" + + BITMAP "....XX.." + BITMAP "....XX.." + BITMAP "...XX..." + BITMAP "...XX..." + BITMAP "..XX...." + BITMAP "..XX...." + BITMAP "...XX..." + BITMAP "...XX..." + + BITMAP ".X.X.X.." + BITMAP "XXXXXXX." + BITMAP ".X.X.X.." + BITMAP "XXXXXXX." + BITMAP ".X.X.X.." + BITMAP "XXXXXXX." + BITMAP ".X.X.X.." + BITMAP "........" + +game_colors: + DATA BYTE $CC,$CC,$CC,$CC,$CC,$CC,$CC,$CC + DATA BYTE $21,$21,$21,$21,$21,$21,$21,$21 + DATA BYTE $A1,$A1,$A1,$A1,$A1,$A1,$A1,$A1 + DATA BYTE $51,$51,$51,$51,$51,$51,$51,$51 + + DATA BYTE $54,$54,$54,$54,$54,$54,$54,$54 + DATA BYTE $51,$51,$51,$51,$51,$51,$51,$51 + DATA BYTE $71,$11,$B1,$B1,$B1,$11,$11,$11 + DATA BYTE $51,$51,$51,$51,$51,$51,$51,$51 + + DATA BYTE $38,$38,$38,$38,$38,$38,$38,$38 + DATA BYTE $81,$81,$81,$81,$81,$81,$81,$81 + DATA BYTE $51,$51,$51,$51,$51,$51,$51,$51 + DATA BYTE $51,$51,$51,$51,$51,$51,$51,$51 + + DATA BYTE $31,$31,$31,$31,$31,$31,$31,$31 + DATA BYTE $A1,$A1,$A1,$A1,$A1,$A1,$A1,$A1 + DATA BYTE $71,$71,$71,$51,$51,$71,$71,$71 + DATA BYTE $B1,$B1,$B1,$B1,$B1,$B1,$B1,$B1 + + DATA BYTE $83,$83,$83,$83,$83,$83,$83,$83 + DATA BYTE $2C,$2C,$2C,$2C,$2C,$2C,$2C,$2C + DATA BYTE $11,$3B,$3B,$3B,$3B,$3B,$3B,$11 + DATA BYTE $C1,$C1,$C1,$C1,$C1,$C1,$C1,$C1 + + DATA BYTE $71,$71,$71,$71,$71,$71,$71,$71 + +letter_colors: + DATA BYTE $71,$71,$71,$71,$71,$71,$71,$71 + +game_palette: + DATA $0000 ' 0 + DATA $0000 ' 1 + DATA $0700 ' 2 Green + DATA $0250 ' 3 Red + DATA $0057 ' 4 Blue + DATA $0427 ' 5 Light blue + DATA $0507 ' 6 Player suit + DATA $0777 ' 7 White + DATA $0371 ' 8 Light red + DATA $0464 ' 9 Player face + DATA $0570 ' 10 Yellow + DATA $0555 ' 11 Gray + DATA $0400 ' 12 Dark green + DATA $0777 ' 13 Snake tongue + DATA $0600 ' 14 Snake body + DATA $0000 ' 15 Black border for sprites + +game_sprites: + BITMAP ".......X.X.X...." + BITMAP ".....XXXXX.XX..." + BITMAP "....XXX.....X..." + BITMAP ".....XX.....X..." + BITMAP "....XX....X.X..." + BITMAP ".....XX.X...X..." + BITMAP "....XXX.XXX.X..." + BITMAP "..XXXXXX...X...." + BITMAP ".XXXXXXXXXXXX..." + BITMAP "XXXXXXXXXXXXXX.." + BITMAP "XXXXXXXXXXXXXX.." + BITMAP "XXXXXXXXXXXXX..." + BITMAP ".XXXXXXXXXXXX..." + BITMAP "...XXXXXXXXXXX.." + BITMAP "...XXXXXXXXXXXX." + BITMAP "...XXXXXXXXXXX.." + + BITMAP ".......X.X.X...." + BITMAP ".....XXXXXXXX..." + BITMAP "....XXXXXXXXX..." + BITMAP ".....XXXXXXXX..." + BITMAP "....XXXXXXXXX..." + BITMAP ".....XXXXXXXX..." + BITMAP "....X..XXXXXX..." + BITMAP "..X....XXXXX...." + BITMAP ".X..X...XXXXX..." + BITMAP "X..XX....X...X.." + BITMAP "X...X....X...X.." + BITMAP "X...X.XXXXXXX..." + BITMAP ".XXXXX....XXX..." + BITMAP "...X...X.....X.." + BITMAP "...X...XX.....X." + BITMAP "...X....XXXXXX.." + + BITMAP "......XXXXXXXX.." + BITMAP ".....XXX.....X.." + BITMAP "......XX.....X.." + BITMAP ".....XX....X.X.." + BITMAP "......X..X...X.." + BITMAP ".....XXX.XXX.X.." + BITMAP ".....XXXX...X..." + BITMAP "....XXXXXXXX...." + BITMAP "....XXXXXXX....." + BITMAP "....XXXXXXXX...." + BITMAP "....XXXXXXXX...." + BITMAP "....XXXXXXX....." + BITMAP ".....XXXXXX....." + BITMAP ".....XXXX......." + BITMAP ".....XXXXXX....." + BITMAP ".....XXXXXXX...." + + BITMAP "......XXXXXXXX.." + BITMAP ".....XXXXXXXXX.." + BITMAP "......XXXXXXXX.." + BITMAP ".....XXXXXXXXX.." + BITMAP "......XXXXXXXX.." + BITMAP ".....X..XXXXXX.." + BITMAP ".....X..XXXXX..." + BITMAP "....X....XXX...." + BITMAP "....X.....X....." + BITMAP "....X..X...X...." + BITMAP "....X..X...X...." + BITMAP "....X...XXX....." + BITMAP ".....X....X....." + BITMAP ".....X..X......." + BITMAP ".....X....X....." + BITMAP ".....X.....X...." + + BITMAP "....X.X.X......." + BITMAP "...XXXXXXXX....." + BITMAP "...X.....XXX...." + BITMAP "...X.....XX....." + BITMAP "...X.X....XX...." + BITMAP "...X...X.XX....." + BITMAP "...X.XXX..XX...." + BITMAP "....X...XXXXXX.." + BITMAP ".....XXXXXXXXXX." + BITMAP "..XXXXXXXXXXXXXX" + BITMAP "..XXXXXXXXXXXXXX" + BITMAP "...XXX.XXXXXXXXX" + BITMAP "...XXXXXXXXXXXX." + BITMAP "..XXXXXXXXXXX..." + BITMAP ".XXXXXXXXXXXX..." + BITMAP "..XXXXXXXXXXX..." + + BITMAP "....X.X.X......." + BITMAP "...XXXXXXXX....." + BITMAP "...XXXXXXXXX...." + BITMAP "...XXXXXXXX....." + BITMAP "...XXXXXXXXX...." + BITMAP "...XXXXXXXX....." + BITMAP "...XXXXXXX.X...." + BITMAP "....XXXXX....X.." + BITMAP ".....XXX...X..X." + BITMAP "..X...X....XX..X" + BITMAP "..X...X....X...X" + BITMAP "...XXX.XXX.X...X" + BITMAP "...XXX....XXXXX." + BITMAP "..X.....X...X..." + BITMAP ".X.....XX...X..." + BITMAP "..XXXXXX....X..." + + BITMAP "..XXXXXXXX......" + BITMAP "..X.....XXX....." + BITMAP "..X.....XX......" + BITMAP "..X.X....XX....." + BITMAP "..X...X.XX......" + BITMAP "..X.XXX..XX....." + BITMAP "...X...XXXX....." + BITMAP "....XXXXXXXX...." + BITMAP ".....XXXXXXX...." + BITMAP "....XXXXXXXX...." + BITMAP "....XXXXXXXX...." + BITMAP ".....XXXXXXX...." + BITMAP ".....XXXXXX....." + BITMAP "......XXXXX....." + BITMAP ".....XXXXXX....." + BITMAP "....XXXXXXX....." + + BITMAP "..XXXXXXXX......" + BITMAP "..XXXXXXXXX....." + BITMAP "..XXXXXXXX......" + BITMAP "..XXXXXXXXX....." + BITMAP "..XXXXXXXX......" + BITMAP "..XXXXXXXXX....." + BITMAP "...XXXXX..X....." + BITMAP "....XXX....X...." + BITMAP ".....X.....X...." + BITMAP "....X...X..X...." + BITMAP "....X...X..X...." + BITMAP ".....XXX...X...." + BITMAP ".....X....X....." + BITMAP "......XX..X....." + BITMAP ".....X....X....." + BITMAP "....X.....X....." + + BITMAP "...XXXXXXXXX...." + BITMAP "....XXXXXXX....." + BITMAP "...XXXXXXXXX...." + BITMAP "...XXXXXXXXXXXX." + BITMAP "....XXXXXXXXXXXX" + BITMAP "....XXXXXXXXXXXX" + BITMAP ".....X...XXXXXX." + BITMAP "...XXXXXXXXXX..." + BITMAP ".XXXXXXXXXXX...." + BITMAP "XXXXX.XXXX......" + BITMAP "XXXXXXXXXXX....." + BITMAP ".XXX.XXXXXXXX..." + BITMAP "....XXXX.XXXXX.." + BITMAP "...XXXXX.XXXXXX." + BITMAP "..XXXXXX..XXXX.." + BITMAP ".XXXXXXX........" + + BITMAP "...XXXXXXXXX...." + BITMAP "....XXXXXXX....." + BITMAP "...XXXXXXXXX...." + BITMAP "...XXXXXXXXXXXX." + BITMAP "....XXXXXXXX...X" + BITMAP "....XXXXXXXX...X" + BITMAP ".....XXXXXX..XX." + BITMAP "...X..XXX...X..." + BITMAP ".XX..X....XX...." + BITMAP "X...X.XXXX......" + BITMAP "X...XX....X....." + BITMAP ".XXX.X.XX..XX..." + BITMAP "....X..X.X...X.." + BITMAP "...XX..X.X....X." + BITMAP "..X....X..XXXX.." + BITMAP ".X.....X........" + + BITMAP "....XXXXXXXXX..." + BITMAP ".....XXXXXXX...." + BITMAP "....XXXXXXXXX..." + BITMAP ".XXXXXXXXXXXX..." + BITMAP "XXXXXXXXXXXX...." + BITMAP "XXXXXXXXXXXX...." + BITMAP ".XXXXXX...XX...." + BITMAP "...XXXXXXXXXX..." + BITMAP "....XXXXXXXXXXX." + BITMAP "......XXXX.XXXXX" + BITMAP ".....XXXXXXXXXXX" + BITMAP "...XXXXXXXX.XXX." + BITMAP "..XXXXX.XXXX...." + BITMAP ".XXXXX..XXXXX..." + BITMAP "..XXX...XXXXXX.." + BITMAP "........XXXXXXX." + + BITMAP "....XXXXXXXXX..." + BITMAP ".....XXXXXXX...." + BITMAP "....XXXXXXXXX..." + BITMAP ".XXXXXXXXXXXX..." + BITMAP "X...XXXXXXXX...." + BITMAP "X...XXXXXXXX...." + BITMAP ".XX..XXXXXXX...." + BITMAP "...X...XXX..X..." + BITMAP "....XX....X..XX." + BITMAP "......XXXX.X...X" + BITMAP ".....X....XX...X" + BITMAP "...XX..XX.X.XXX." + BITMAP "..X...X.X..X...." + BITMAP ".X...X..X..XX..." + BITMAP "..XXX...X....X.." + BITMAP "........X.....X." + + BITMAP "..X..X.XX......." + BITMAP ".X.X..XXX......." + BITMAP ".X.X..XXX......." + BITMAP ".X..X.XX........" + BITMAP ".XX....XX......." + BITMAP "XX....X.XX......" + BITMAP "XXX..X..XX....X." + BITMAP "...XXX..XX...X.X" + BITMAP "...X...XX....X.X" + BITMAP "..X....XX...X.XX" + BITMAP ".X....XX....X.XX" + BITMAP ".X...XX.XX.X..XX" + BITMAP ".X...XXX..XX.XX." + BITMAP ".X.........X.XX." + BITMAP "..X..........XX." + BITMAP "...XXXXXXXXXXX.." + + BITMAP "..XXXXXXX......." + BITMAP ".XX.XX.XX......." + BITMAP ".XX.XX.XX......." + BITMAP ".XXXXXXX........" + BITMAP ".XXXXXXXX......." + BITMAP "X.XXXXXXXX......" + BITMAP ".XXXXXXXXX....X." + BITMAP "...XXXXXXX...XXX" + BITMAP "...XXXXXX....XXX" + BITMAP "..XXXXXXX...XXXX" + BITMAP ".XXXXXXX....XXXX" + BITMAP ".XXXXXX.XX.XXXXX" + BITMAP ".XXXXXXXXXXXXXX." + BITMAP ".XXXXXXXXXXXXXX." + BITMAP "..XXXXXXXXXXXXX." + BITMAP "...XXXXXXXXXXX.." + + BITMAP "....XX.XX......." + BITMAP "...X..X.XX......" + BITMAP "..X.X..XXX......" + BITMAP "..X.X..XXX......" + BITMAP "..X..X.XX......." + BITMAP "...X....XX......" + BITMAP ".XX....X.XX....." + BITMAP "XXX...X..XX....." + BITMAP "XXXXXXX..XX..X.." + BITMAP ".XXX....XX..X.X." + BITMAP "..X....XXX.X.XX." + BITMAP "..X...XX.X.X.XX." + BITMAP "..X...XX.XX.XX.." + BITMAP "..X.........XX.." + BITMAP "...X.....X..XX.." + BITMAP "....XXXXXXXXX..." + + BITMAP "....XX.XX......." + BITMAP "...XXXXXXX......" + BITMAP "..XX.XX.XX......" + BITMAP "..XX.XX.XX......" + BITMAP "..XXXXXXX......." + BITMAP "...XXXXXXX......" + BITMAP ".XXXXXXXXXX....." + BITMAP "X.XXXXXXXXX....." + BITMAP "X..XXXXXXXX..X.." + BITMAP ".XXXXXXXXX..XXX." + BITMAP "..XXXXXXXX.XXXX." + BITMAP "..XXXXXXXX.XXXX." + BITMAP "..XXXXXXXXXXXX.." + BITMAP "..XXXXXXXXXXXX.." + BITMAP "...XXXXXXXXXXX.." + BITMAP "....XXXXXXXXX..." + + BITMAP ".......XX.X..X.." + BITMAP ".......XXX..X.X." + BITMAP ".......XXX..X.X." + BITMAP "........XX.X..X." + BITMAP ".......XX....XX." + BITMAP "......XX.X....XX" + BITMAP ".X....XX..X..XXX" + BITMAP "X.X...XX..XXX..." + BITMAP "X.X....XX...X..." + BITMAP "XX.X...XX....X.." + BITMAP "XX.X....XX....X." + BITMAP "XX..X.XX.XX...X." + BITMAP ".XX.XX..XXX...X." + BITMAP ".XX.X.........X." + BITMAP ".XX..........X.." + BITMAP "..XXXXXXXXXXX..." + + BITMAP ".......XXXXXXX.." + BITMAP ".......XX.XX.XX." + BITMAP ".......XX.XX.XX." + BITMAP "........XXXXXXX." + BITMAP ".......XXXXXXXX." + BITMAP "......XXXXXXXX.X" + BITMAP ".X....XXXXXXXXX." + BITMAP "XXX...XXXXXXX..." + BITMAP "XXX....XXXXXX..." + BITMAP "XXXX...XXXXXXX.." + BITMAP "XXXX....XXXXXXX." + BITMAP "XXXXX.XX.XXXXXX." + BITMAP ".XXXXXXXXXXXXXX." + BITMAP ".XXXXXXXXXXXXXX." + BITMAP ".XXXXXXXXXXXXX.." + BITMAP "..XXXXXXXXXXX..." + + BITMAP ".......XX.XX...." + BITMAP "......XX.X..X..." + BITMAP "......XXX..X.X.." + BITMAP "......XXX..X.X.." + BITMAP ".......XX.X..X.." + BITMAP "......XX....X..." + BITMAP ".....XX.X....X.." + BITMAP ".....XX..X...XXX" + BITMAP "..X..XX..XXXXXXX" + BITMAP ".X.X..XX....X..." + BITMAP ".XX.X.XXX....X.." + BITMAP ".XX.XX..XX...X.." + BITMAP "..XX.X..XX...X.." + BITMAP "..XX.........X.." + BITMAP "..XX..X.....X..." + BITMAP "...XXXXXXXXX...." + + BITMAP ".......XX.XX...." + BITMAP "......XXXXXXX..." + BITMAP "......XX.XX.XX.." + BITMAP "......XX.XX.XX.." + BITMAP ".......XXXXXXX.." + BITMAP "......XXXXXXX..." + BITMAP ".....XXXXXXXXX.." + BITMAP ".....XXXXXXXXX.X" + BITMAP "..X..XXXXXXXX..X" + BITMAP ".XXX..XXXXXXX..." + BITMAP ".XXXX.XXXXXXXX.." + BITMAP ".XXXXXXXXXXXXX.." + BITMAP "..XXXXXXXXXXXX.." + BITMAP "..XXXXXXXXXXXX.." + BITMAP "..XXXXXXXXXXX..." + BITMAP "...XXXXXXXXX...." + + ' + ' This is the color table used for sprites on the screen. + ' + ' The player is composed by colors 6 and 9. The color 9 has the bit 6 set + ' so it is OR'ed with the sprite below, and when both pixels are set it + ' creates color 15 (6 OR 9 = 15) + ' + ' The snakes are composed by colors 13 and 14. The color 14 has the bit 6 set + ' so it is OR'ed with the sprite below, and when both pixels are set it + ' creates color 15 (13 OR 14 = 15) + ' +sprites_color: + DATA BYTE $06,$06,$06,$06,$06,$06,$06,$06 + DATA BYTE $06,$06,$06,$06,$06,$06,$06,$06 + DATA BYTE $49,$49,$49,$49,$49,$49,$49,$49 + DATA BYTE $49,$49,$49,$49,$49,$49,$49,$49 + DATA BYTE $0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d + DATA BYTE $0d,$0d,$0d,$0d,$0d,$0d,$0d,$0d + DATA BYTE $4e,$4e,$4e,$4e,$4e,$4e,$4e,$4e + DATA BYTE $4e,$4e,$4e,$4e,$4e,$4e,$4e,$4e + +start_song: PROCEDURE + tick_note = 8 + song_note = 47 + END + +play_song: PROCEDURE + tick_note = tick_note + 1. + IF tick_note = 16. THEN + tick_note = 0. + song_note = song_note + 1. + IF song_note = 48. THEN song_note = 0. + note = song_notes(song_note) + SOUND 5, #note_freq(note - 1) + END IF + SOUND 5, , volume_effect(tick_note) + END + +sound_off: PROCEDURE + SOUND 5,,0 + SOUND 6,,0 + SOUND 7,,0 + SOUND 9,,$38 + END + +volume_effect: + DATA BYTE 11,12,13,12,12,11,11,10 + DATA BYTE 10,9,9,10,10,9,9,8 + +song_notes: + DATA BYTE 1,2,3,4,5,4,3,2 + DATA BYTE 1,2,3,4,5,4,3,2 + DATA BYTE 6,4,7,8,9,8,7,4 + DATA BYTE 6,4,7,8,9,8,7,4 + DATA BYTE 3,12,8,10,11,10,8,12 + DATA BYTE 6,4,7,8,9,8,7,4 + +#note_freq: + DATA $01AC + DATA $0153 + DATA $011D + DATA $00FE + DATA $00F0 + DATA $0140 + DATA $00D6 + DATA $00BE + DATA $00B4 + DATA $00AA + DATA $00A0 + DATA $00E2 diff --git a/examples/viboritas_nes.bas b/examples/viboritas_nes.bas new file mode 100644 index 0000000..fb7b793 --- /dev/null +++ b/examples/viboritas_nes.bas @@ -0,0 +1,731 @@ + ' + ' Viboritas (demo for CVBasic) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Oct/1990. + ' Revision date: Feb/29/2024. Ported to CVBasic. + ' Revision date: Aug/23/2025. Adapted to NES. + ' + + ' The original game was made in Z80 assembler, + ' you can see it here: https://nanochess.org/viboritas.html + + ' It is easier to understand in CVBasic ;) + + DIM #ladders(15) + +restart_game: + lives = 2 + level = 1 +restart_level: + + PRINT AT 780,"Lives: ",lives + PRINT AT 841,"nanochess 1990" + + ' Palette for the player + PALETTE 17,$12 + PALETTE 18,$2c + PALETTE 19,$36 + + ' Palette for snakes + PALETTE 21,$0F + PALETTE 22,$30 + PALETTE 23,$2a + +next_level: + GOSUB draw_level + + x_player = 8 + y_player = 16 + player_frame = 0 + + x_enemy1 = random(128) + 64 + y_enemy1 = 56 + enemy1_frame = 24 + x_enemy2 = random(128) + 80 + y_enemy2 = 96 + enemy2_frame = 32 + x_enemy3 = random(128) + 48 + y_enemy3 = 136 + enemy3_frame = 24 + + enemy_speed = 0 + + GOSUB start_song + +game_loop: + WHILE 1 + WAIT + GOSUB play_song + + SPRITE 0, y_player + 23, x_player, player_frame + 1, 0 + SPRITE 1, y_player + 23, x_player + 8, player_frame + 3, 0 + SPRITE 2, y_enemy1 + 23, x_enemy1, enemy1_frame + 1, 1 + SPRITE 3, y_enemy1 + 23, x_enemy1 + 8, enemy1_frame + 3, 1 + SPRITE 4, y_enemy2 + 23, x_enemy2, enemy2_frame + 1, 1 + SPRITE 5, y_enemy2 + 23, x_enemy2 + 8, enemy2_frame + 3, 1 + SPRITE 6, y_enemy3 + 23, x_enemy3, enemy3_frame + 1, 1 + SPRITE 7, y_enemy3 + 23, x_enemy3 + 8, enemy3_frame + 3, 1 + + GOSUB move_player + + c = $50 + level * 4 + enemy_speed = enemy_speed + c + WHILE enemy_speed >= $40 + enemy_speed = enemy_speed - $40 + GOSUB move_enemies + WEND + IF cont1.button THEN + IF x_player > 232 AND x_player < 248 AND y_player = 136 THEN + GOSUB sound_off + + FOR c = 1 to 10 + WAIT + SOUND 10, 200 - c * 10, 13 + 16 + NEXT c + + level = level + 1 + IF level = 6 THEN + GOSUB sound_off + PRINT AT 267," YOU WIN! " + #c = FRAME + DO + WAIT + LOOP WHILE FRAME - #c < 300 + level = 1 + GOTO restart_level + END IF + GOTO next_level + END IF + END IF + IF ABS(y_player + 1 - y_enemy1) < 8 THEN + IF ABS(x_player + 1 - x_enemy1) < 8 THEN GOTO player_dies + END IF + IF ABS(y_player + 1 - y_enemy2) < 8 THEN + IF ABS(x_player + 1 - x_enemy2) < 8 THEN GOTO player_dies + END IF + IF ABS(y_player + 1 - y_enemy3) < 8 THEN + IF ABS(x_player + 1 - x_enemy3) < 8 THEN GOTO player_dies + END IF + WEND + +player_dies: + GOSUB sound_off + + SOUND 10,640,13 + 48 + SOUND 11,320,13 + 48 + + player_frame = 0 + FOR c = 0 TO 30 + WAIT + WAIT + player_frame = player_frame XOR 8 + SPRITE 0, y_player + 23, x_player, player_frame + 1, 0 + SPRITE 1, y_player + 23, x_player + 8, player_frame + 3, 0 + NEXT c + + GOSUB sound_off + + DO + WAIT + SOUND 10,200 - y_player,13 + 48 + player_frame = player_frame XOR 8 + SPRITE 0, y_player + 23, x_player, player_frame + 1, 0 + SPRITE 1, y_player + 23, x_player + 8, player_frame + 3, 0 + y_player = y_player + 2 + LOOP WHILE y_player < 160 + + GOSUB sound_off + + IF lives = 0 THEN + PRINT AT 267," GAME OVER " + #c = FRAME + DO + WAIT + LOOP WHILE FRAME - #c < 300 + GOTO restart_game + END IF + lives = lives - 1 + GOTO restart_level + + ' + ' Draw the current level. + ' +draw_level: PROCEDURE + + FOR c = 0 TO 63 + SPRITE c, $f0, 0, 0, 0 + NEXT c + + ' Clean the screen in a curtain to hide palette change + FOR #c = $2060 TO $207F + FOR #d = #c TO #c + $280 STEP 32 + VPOKE #d, 32 + NEXT #d + WAIT + NEXT #c + + c = (level - 1) * 4 + PALETTE 4, palette_screens(c) + PALETTE 5, palette_screens(c + 1) + PALETTE 6, palette_screens(c + 2) + PALETTE 7, palette_screens(c + 3) + + ' Select palette 1 for game tiles + ' Indicators in the bottom are still in palette 0 + FOR #c = $23c0 TO $23ef + VPOKE #c, $55 + NEXT #c + + ' Get the base character to draw the level. + base_character = 128 + (level - 1) * 4 + + ' Draw the background. + FOR #c = $2060 TO $22DC STEP 4 + VPOKE #c, base_character + VPOKE #c + 1, base_character + VPOKE #c + 2, base_character + VPOKE #c + 3, base_character + 1 + NEXT #c + + ' Draw over the floors. + FOR #c = $20e0 TO $22c0 STEP 160 + FOR #d = #c TO #c + 31 + VPOKE #d, base_character + 2. + NEXT #d + NEXT #c + + ' Draw the ladders. + ladders = 6 - level + + next_ladder = 0 + + FOR #c = $20e0 TO $2220 STEP 160 + FOR d = 1 TO ladders + e = (RANDOM(28) + 2) + #ladders(next_ladder) = #c + e + next_ladder = next_ladder + 1 + VPOKE #c + e, base_character + 3. + VPOKE #c + e + 32, base_character + 3. + VPOKE #c + e + 64, base_character + 3. + VPOKE #c + e + 96, base_character + 3. + VPOKE #c + e + 128, base_character + 3. + NEXT d + NEXT #c + + ' Draw the "exit". + VPOKE $22be, 148 + + END + + ' + ' Move the player + ' +move_player: PROCEDURE + IF cont1.left THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor + IF x_player > 0 THEN x_player = x_player - 1 + IF FRAME AND 4 THEN player_frame = 8 ELSE player_frame = 12 + END IF + END IF + IF cont1.right THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor + IF x_player < 240 THEN x_player = x_player + 1 + IF FRAME AND 4 THEN player_frame = 0 ELSE player_frame = 4 + END IF + END IF + IF cont1.up THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor. + column = (x_player + 7) /8 + row = (y_player + 8) / 8 + #c = $2060 + row * 32 + column + FOR c = 0 TO next_ladder - 1 + IF #c >= #ladders(c) THEN + #d = #c - #ladders(c) + IF (#d AND $1F) = 0 THEN ' Centered horizontally? + IF #d < $a0 THEN ' Over ladder? + y_player = y_player - 1 + END IF + END IF + END IF + NEXT c + ELSE + IF FRAME AND 4 THEN player_frame = 16 ELSE player_frame = 20 + y_player = y_player - 1 + END IF + END IF + IF cont1.down THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor. + column = (x_player + 7) /8 + row = (y_player + 16) / 8 + #c = $2060 + row * 32 + column + FOR c = 0 TO next_ladder - 1 + IF #c >= #ladders(c) THEN + #d = #c - #ladders(c) + IF (#d AND $1F) = 0 THEN ' Centered horizontally? + IF #d < $a0 THEN ' Over ladder? + y_player = y_player + 1 + END IF + END IF + END IF + NEXT c + ELSE + IF FRAME AND 4 THEN player_frame = 16 ELSE player_frame = 20 + y_player = y_player + 1 + END IF + END IF + END + + ' + ' Move the enemies. + ' +move_enemies: PROCEDURE + IF enemy1_frame < 32 THEN + x_enemy1 = x_enemy1 - 1. + IF x_enemy1 = 0 THEN enemy1_frame = 32 + ELSE + x_enemy1 = x_enemy1 + 1. + IF x_enemy1 = 240 THEN enemy1_frame = 24 + END IF + enemy1_frame = (enemy1_frame AND $f8) + (FRAME AND 4) + + IF enemy2_frame < 32 THEN + x_enemy2 = x_enemy2 - 1. + IF x_enemy2 = 0 THEN enemy2_frame = 32 + ELSE + x_enemy2 = x_enemy2 + 1. + IF x_enemy2 = 240 THEN enemy2_frame = 24 + END IF + enemy2_frame = (enemy2_frame AND $f8) + (FRAME AND 4) + + IF enemy3_frame < 32 THEN + x_enemy3 = x_enemy3 - 1. + IF x_enemy3 = 0 THEN enemy3_frame = 32 + ELSE + x_enemy3 = x_enemy3 + 1. + IF x_enemy3 = 240 THEN enemy3_frame = 24 + END IF + enemy3_frame = (enemy3_frame AND $f8) + (FRAME AND 4) + END + +palette_screens: + DATA BYTE $0F,$2A,$2C,$28 + DATA BYTE $0F,$02,$12,$10 + DATA BYTE $0F,$07,$17,$12 + DATA BYTE $0F,$17,$27,$10 + DATA BYTE $0F,$16,$2A,$10 + + CHRROM 0 + + CHRROM PATTERN 128 + + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + BITMAP "11111111" + + BITMAP "111..111" + BITMAP "111..111" + BITMAP "111..111" + BITMAP "111..111" + BITMAP "111..111" + BITMAP "111..111" + BITMAP "111..111" + BITMAP "111..111" + + BITMAP "33333333" + BITMAP "33333333" + BITMAP "........" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "........" + BITMAP "33333333" + BITMAP "33333333" + + BITMAP ".2....2." + BITMAP ".2....2." + BITMAP ".222222." + BITMAP ".2....2." + BITMAP ".2....2." + BITMAP ".222222." + BITMAP ".2....2." + BITMAP ".2....2." + + BITMAP "22222221" + BITMAP "21111121" + BITMAP "21222121" + BITMAP "21212121" + BITMAP "21222121" + BITMAP "21111121" + BITMAP "22222221" + BITMAP "11111111" + + BITMAP "2.222.2." + BITMAP "2.222.2." + BITMAP "2.222.2." + BITMAP "2.222.2." + BITMAP "2.222.2." + BITMAP "2.222.2." + BITMAP "2.222.2." + BITMAP "2.222.2." + + BITMAP "333.333." + BITMAP "........" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP ".1....1." + BITMAP ".1....1." + BITMAP ".111111." + BITMAP ".1....1." + BITMAP ".1....1." + BITMAP ".111111." + BITMAP ".1....1." + BITMAP ".1....1." + + BITMAP "11121111" + BITMAP "11121111" + BITMAP "11121111" + BITMAP "22222222" + BITMAP "11111112" + BITMAP "11111112" + BITMAP "11111112" + BITMAP "22222222" + + BITMAP ".222222." + BITMAP ".222222." + BITMAP ".222222." + BITMAP "........" + BITMAP ".22.222." + BITMAP ".22.222." + BITMAP ".22.222." + BITMAP "........" + + BITMAP "........" + BITMAP "33333333" + BITMAP "33333333" + BITMAP "3.3.3.3." + BITMAP ".3...3.." + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP ".3....3." + BITMAP ".3....3." + BITMAP ".333333." + BITMAP ".3....3." + BITMAP ".3....3." + BITMAP ".333333." + BITMAP ".3....3." + BITMAP ".3....3." + + BITMAP "111.111." + BITMAP "111.111." + BITMAP "111.111." + BITMAP "........" + BITMAP "111.111." + BITMAP "111.111." + BITMAP "111.111." + BITMAP "........" + + BITMAP ".2......" + BITMAP "..22...." + BITMAP "....22.." + BITMAP "......22" + BITMAP "....22.." + BITMAP "..22...." + BITMAP ".2......" + BITMAP ".2......" + + BITMAP "........" + BITMAP "33333333" + BITMAP "........" + BITMAP "1.1.1.1." + BITMAP ".1.1.1.1" + BITMAP "........" + BITMAP "33333333" + BITMAP "........" + + BITMAP "3......3" + BITMAP "3......3" + BITMAP "33....33" + BITMAP "3.3333.3" + BITMAP "3......3" + BITMAP "3......3" + BITMAP "33....33" + BITMAP "3.3333.3" + + BITMAP "1......1" + BITMAP ".1.11..." + BITMAP "..11.111" + BITMAP ".1...111" + BITMAP "..111..1" + BITMAP "..1..111" + BITMAP ".1..1..1" + BITMAP "..1..111" + + BITMAP ".2...222" + BITMAP ".2..2..2" + BITMAP "..2..222" + BITMAP ".2......" + BITMAP "..2.2..." + BITMAP "...2.2.2" + BITMAP "...2..2." + BITMAP "..2..222" + + BITMAP "........" + BITMAP "11111113" + BITMAP "11111113" + BITMAP "33333333" + BITMAP "11131111" + BITMAP "11131111" + BITMAP "33333333" + BITMAP "........" + + BITMAP "....22.." + BITMAP "....22.." + BITMAP "...22..." + BITMAP "...22..." + BITMAP "..22...." + BITMAP "..22...." + BITMAP "...22..." + BITMAP "...22..." + + BITMAP ".3.3.3.." + BITMAP "3333333." + BITMAP ".3.3.3.." + BITMAP "3333333." + BITMAP ".3.3.3.." + BITMAP "3333333." + BITMAP ".3.3.3.." + BITMAP "........" + + CHRROM PATTERN 256 + + BITMAP ".......1.1.1...." + BITMAP ".....11111311..." + BITMAP "....111333331..." + BITMAP ".....11333331..." + BITMAP "....113333131..." + BITMAP ".....11313331..." + BITMAP "....122311131..." + BITMAP "..1222213331...." + BITMAP ".122122211111..." + BITMAP "12211222212221.." + BITMAP "12221222212221.." + BITMAP "1222121111111..." + BITMAP ".111112222111..." + BITMAP "...12221222221.." + BITMAP "...122211222221." + BITMAP "...12222111111.." + + BITMAP "......11111111.." + BITMAP ".....111333331.." + BITMAP "......11333331.." + BITMAP ".....113333131.." + BITMAP "......13313331.." + BITMAP ".....122311131.." + BITMAP ".....12213331..." + BITMAP "....12222111...." + BITMAP "....1222221....." + BITMAP "....12212221...." + BITMAP "....12212221...." + BITMAP "....1222111....." + BITMAP ".....122221....." + BITMAP ".....1221......." + BITMAP ".....122221....." + BITMAP ".....1222221...." + + BITMAP "....1.1.1......." + BITMAP "...11111111....." + BITMAP "...133333111...." + BITMAP "...13333311....." + BITMAP "...131333311...." + BITMAP "...13331311....." + BITMAP "...131113321...." + BITMAP "....1333122221.." + BITMAP ".....1112221221." + BITMAP "..12221222211221" + BITMAP "..12221222212221" + BITMAP "...111.111212221" + BITMAP "...111222211111." + BITMAP "..12222212221..." + BITMAP ".122222112221..." + BITMAP "..11111122221..." + + BITMAP "..11111111......" + BITMAP "..133333111....." + BITMAP "..13333311......" + BITMAP "..131333311....." + BITMAP "..13331311......" + BITMAP "..131113311....." + BITMAP "...13331221....." + BITMAP "....11122221...." + BITMAP ".....1222221...." + BITMAP "....12221221...." + BITMAP "....12221221...." + BITMAP ".....1112221...." + BITMAP ".....122221....." + BITMAP "......11221....." + BITMAP ".....122221....." + BITMAP "....1222221....." + + BITMAP "...111111111...." + BITMAP "....1111111....." + BITMAP "...111111111...." + BITMAP "...111111111111." + BITMAP "....111111112221" + BITMAP "....111111112221" + BITMAP ".....1333112211." + BITMAP "...1221112221..." + BITMAP ".11221222211...." + BITMAP "12221.1111......" + BITMAP "12221122221....." + BITMAP ".111.12112211..." + BITMAP "....1221.12221.." + BITMAP "...11221.122221." + BITMAP "..122221..1111.." + BITMAP ".1222221........" + + BITMAP "....111111111..." + BITMAP ".....1111111...." + BITMAP "....111111111..." + BITMAP ".111111111111..." + BITMAP "122211111111...." + BITMAP "122211111111...." + BITMAP ".11221133311...." + BITMAP "...1222111221..." + BITMAP "....11222212211." + BITMAP "......1111.12221" + BITMAP ".....12222112221" + BITMAP "...11221121.111." + BITMAP "..12221.1221...." + BITMAP ".12221..12211..." + BITMAP "..111...122221.." + BITMAP "........1222221." + + BITMAP "..1331311......." + BITMAP ".13233211......." + BITMAP ".13233211......." + BITMAP ".1331311........" + BITMAP ".11333311......." + BITMAP "1233331311......" + BITMAP "2113313311....1." + BITMAP "...1113311...131" + BITMAP "...133311....131" + BITMAP "..1333311...1311" + BITMAP ".1333311....1311" + BITMAP ".133311.11.13311" + BITMAP ".13331113311311." + BITMAP ".13333333331311." + BITMAP "..1333333333311." + BITMAP "...11111111111.." + + BITMAP "....11.11......." + BITMAP "...1331311......" + BITMAP "..13233211......" + BITMAP "..13233211......" + BITMAP "..1331311......." + BITMAP "...1333311......" + BITMAP ".1133331311....." + BITMAP "12133313311....." + BITMAP "12211113311..1.." + BITMAP ".111333311..131." + BITMAP "..13333111.1311." + BITMAP "..13331131.1311." + BITMAP "..133311311311.." + BITMAP "..133333333311.." + BITMAP "...13333313311.." + BITMAP "....111111111..." + + BITMAP ".......1131331.." + BITMAP ".......11233231." + BITMAP ".......11233231." + BITMAP "........1131331." + BITMAP ".......11333311." + BITMAP "......1131333321" + BITMAP ".1....1133133112" + BITMAP "131...1133111..." + BITMAP "131....113331..." + BITMAP "1131...1133331.." + BITMAP "1131....1133331." + BITMAP "11331.11.113331." + BITMAP ".11311331113331." + BITMAP ".11313333333331." + BITMAP ".1133333333331.." + BITMAP "..11111111111..." + + BITMAP ".......11.11...." + BITMAP "......1131331..." + BITMAP "......11233231.." + BITMAP "......11233231.." + BITMAP ".......1131331.." + BITMAP "......1133331..." + BITMAP ".....113133331.." + BITMAP ".....11331333121" + BITMAP "..1..11331111221" + BITMAP ".131..1133331..." + BITMAP ".1131.11133331.." + BITMAP ".1131133113331.." + BITMAP "..113133113331.." + BITMAP "..113333333331.." + BITMAP "..11331333331..." + BITMAP "...111111111...." + +start_song: PROCEDURE + tick_note = 8 + song_note = 47 + END + +play_song: PROCEDURE + tick_note = tick_note + 1. + IF tick_note = 16. THEN + tick_note = 0. + song_note = song_note + 1. + IF song_note = 48. THEN song_note = 0. + note = song_notes(song_note) + SOUND 10, #note_freq(note - 1) + END IF + SOUND 10, , volume_effect(tick_note) + 48 + END + +sound_off: PROCEDURE + SOUND 10,,16 + SOUND 11,,16 + END + +volume_effect: + DATA BYTE 11,12,13,12,12,11,11,10 + DATA BYTE 10,9,9,10,10,9,9,8 + +song_notes: + DATA BYTE 1,2,3,4,5,4,3,2 + DATA BYTE 1,2,3,4,5,4,3,2 + DATA BYTE 6,4,7,8,9,8,7,4 + DATA BYTE 6,4,7,8,9,8,7,4 + DATA BYTE 3,12,8,10,11,10,8,12 + DATA BYTE 6,4,7,8,9,8,7,4 + +#note_freq: + DATA $01AC + DATA $0153 + DATA $011D + DATA $00FE + DATA $00F0 + DATA $0140 + DATA $00D6 + DATA $00BE + DATA $00B4 + DATA $00AA + DATA $00A0 + DATA $00E2 diff --git a/examples/viboritas_sms.bas b/examples/viboritas_sms.bas new file mode 100644 index 0000000..5683ddd --- /dev/null +++ b/examples/viboritas_sms.bas @@ -0,0 +1,672 @@ + ' + ' Viboritas (demo for CVBasic) + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Oct/1990. + ' Revision date: Feb/29/2024. Ported to CVBasic. + ' Revision date: Apr/26/2025. Adapted to Sega Master System. + ' + + ' The original game was made in Z80 assembler, + ' you can see it here: https://nanochess.org/viboritas.html + + ' It is easier to understand in CVBasic ;) + + DEFINE CHAR 128,21,game_bitmaps + DEFINE SPRITE 0,20,game_sprites + +restart_game: + lives = 2 + level = 1 +restart_level: + + PRINT AT 684,"Lives: ",lives + PRINT AT 745,"nanochess 1990" + +next_level: + GOSUB draw_level + + x_player = 8 + y_player = 16 + player_frame = 0 + + x_enemy1 = random(128) + 64 + y_enemy1 = 56 + enemy1_frame = 24 + x_enemy2 = random(128) + 80 + y_enemy2 = 96 + enemy2_frame = 32 + x_enemy3 = random(128) + 48 + y_enemy3 = 136 + enemy3_frame = 24 + + enemy_speed = 0 + + GOSUB start_song + +game_loop: + WHILE 1 + WAIT + GOSUB play_song + + SPRITE 0, y_player - 1, x_player, player_frame + SPRITE 1, y_player - 1, x_player + 8, player_frame + 2 + SPRITE 2, y_enemy1 - 1, x_enemy1, enemy1_frame + SPRITE 3, y_enemy1 - 1, x_enemy1 + 8, enemy1_frame + 2 + SPRITE 4, y_enemy2 - 1, x_enemy2, enemy2_frame + SPRITE 5, y_enemy2 - 1, x_enemy2 + 8, enemy2_frame + 2 + SPRITE 6, y_enemy3 - 1, x_enemy3, enemy3_frame + SPRITE 7, y_enemy3 - 1, x_enemy3 + 8, enemy3_frame + 2 + + GOSUB move_player + + c = $50 + level * 4 + enemy_speed = enemy_speed + c + WHILE enemy_speed >= $40 + enemy_speed = enemy_speed - $40 + GOSUB move_enemies + WEND + IF cont1.button THEN + IF x_player > 232 AND x_player < 248 AND y_player = 136 THEN + GOSUB sound_off + + FOR c = 1 to 10 + WAIT + SOUND 0, 200 - c * 10, 13 + NEXT c + + level = level + 1 + IF level = 6 THEN + GOSUB sound_off + PRINT AT 267," YOU WIN! " + #c = FRAME + DO + WAIT + LOOP WHILE FRAME - #c < 300 + level = 1 + GOTO restart_level + END IF + GOTO next_level + END IF + END IF + IF ABS(y_player + 1 - y_enemy1) < 8 THEN + IF ABS(x_player + 1 - x_enemy1) < 8 THEN GOTO player_dies + END IF + IF ABS(y_player + 1 - y_enemy2) < 8 THEN + IF ABS(x_player + 1 - x_enemy2) < 8 THEN GOTO player_dies + END IF + IF ABS(y_player + 1 - y_enemy3) < 8 THEN + IF ABS(x_player + 1 - x_enemy3) < 8 THEN GOTO player_dies + END IF + WEND + +player_dies: + GOSUB sound_off + + SOUND 0,640,13 + SOUND 1,320,13 + SOUND 2,160,13 + + player_frame = 0 + FOR c = 0 TO 30 + WAIT + WAIT + player_frame = player_frame XOR 8 + SPRITE 0, y_player - 1, x_player, player_frame + SPRITE 1, y_player - 1, x_player + 8, player_frame + 2 + NEXT c + + GOSUB sound_off + + DO + WAIT + SOUND 0,200 - y_player,13 + player_frame = player_frame XOR 8 + SPRITE 0, y_player - 1, x_player, player_frame + SPRITE 1, y_player - 1, x_player + 8, player_frame + 2 + y_player = y_player + 2 + LOOP WHILE y_player < 160 + + GOSUB sound_off + + IF lives = 0 THEN + PRINT AT 267," GAME OVER " + #c = FRAME + DO + WAIT + LOOP WHILE FRAME - #c < 300 + GOTO restart_game + END IF + lives = lives - 1 + GOTO restart_level + + ' + ' Draw the current level. + ' +draw_level: PROCEDURE + + ' Get the base character to draw the level. + base_character = 128 + (level - 1) * 4 + + ' Draw the background. + FOR #c = $3800 TO $3cf8 STEP 8 + VPOKE #c, base_character + VPOKE #c + 2, base_character + VPOKE #c + 4, base_character + VPOKE #c + 6, base_character + 1. + NEXT #c + + ' Draw over the floors. + FOR #c = $3900 TO $3cc0 STEP 320 + FOR #d = #c TO #c + 62 STEP 2 + VPOKE #d, base_character + 2. + NEXT #d + NEXT #c + + ' Draw the ladders. + ladders = 6 - level + + FOR #c = $3900 TO $3B80 STEP 320 + FOR d = 1 TO ladders + e = (RANDOM(28) + 2) * 2 + VPOKE #c + e, base_character + 3. + VPOKE #c + e + 64, base_character + 3. + VPOKE #c + e + 128, base_character + 3. + VPOKE #c + e + 192, base_character + 3. + VPOKE #c + e + 256, base_character + 3. + NEXT d + NEXT #c + + ' Draw the "exit". + VPOKE $3CBC, 148 + + END + + ' + ' Move the player + ' +move_player: PROCEDURE + IF cont1.left THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor + IF x_player > 0 THEN x_player = x_player - 1 + IF FRAME AND 4 THEN player_frame = 8 ELSE player_frame = 12 + END IF + END IF + IF cont1.right THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor + IF x_player < 240 THEN x_player = x_player + 1 + IF FRAME AND 4 THEN player_frame = 0 ELSE player_frame = 4 + END IF + END IF + IF cont1.up THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor. + column = (x_player + 7) /8 + row = (y_player + 8) / 8 + #c = $3800 + row * 64 + column * 2 + IF VPEEK(#c) = base_character + 3 THEN ' Ladder? + y_player = y_player - 1 + END IF + ELSE + IF FRAME AND 4 THEN player_frame = 16 ELSE player_frame = 20 + y_player = y_player - 1 + END IF + END IF + IF cont1.down THEN + IF y_player % 40 = 16 THEN ' Player aligned on floor. + column = (x_player + 7) /8 + row = (y_player + 16) / 8 + #c = $3800 + row * 64 + column * 2 + IF VPEEK(#c) = base_character + 3 THEN ' Ladder? + y_player = y_player + 1 + END IF + ELSE + IF FRAME AND 4 THEN player_frame = 16 ELSE player_frame = 20 + y_player = y_player + 1 + END IF + END IF + END + + ' + ' Move the enemies. + ' +move_enemies: PROCEDURE + IF enemy1_frame < 32 THEN + x_enemy1 = x_enemy1 - 1. + IF x_enemy1 = 0 THEN enemy1_frame = 32 + ELSE + x_enemy1 = x_enemy1 + 1. + IF x_enemy1 = 240 THEN enemy1_frame = 24 + END IF + enemy1_frame = (enemy1_frame AND $f8) + (FRAME AND 4) + + IF enemy2_frame < 32 THEN + x_enemy2 = x_enemy2 - 1. + IF x_enemy2 = 0 THEN enemy2_frame = 32 + ELSE + x_enemy2 = x_enemy2 + 1. + IF x_enemy2 = 240 THEN enemy2_frame = 24 + END IF + enemy2_frame = (enemy2_frame AND $f8) + (FRAME AND 4) + + IF enemy3_frame < 32 THEN + x_enemy3 = x_enemy3 - 1. + IF x_enemy3 = 0 THEN enemy3_frame = 32 + ELSE + x_enemy3 = x_enemy3 + 1. + IF x_enemy3 = 240 THEN enemy3_frame = 24 + END IF + enemy3_frame = (enemy3_frame AND $f8) + (FRAME AND 4) + END + +game_bitmaps: + BITMAP "CCCCCCCC" + BITMAP "CCCCCCCC" + BITMAP "CCCCCCCC" + BITMAP "CCCCCCCC" + BITMAP "CCCCCCCC" + BITMAP "CCCCCCCC" + BITMAP "CCCCCCCC" + BITMAP "CCCCCCCC" + + BITMAP "222..222" + BITMAP "222..222" + BITMAP "222..222" + BITMAP "222..222" + BITMAP "222..222" + BITMAP "222..222" + BITMAP "222..222" + BITMAP "222..222" + + BITMAP "AAAAAAAA" + BITMAP "AAAAAAAA" + BITMAP "........" + BITMAP "AAAAAAAA" + BITMAP "AAAAAAAA" + BITMAP "........" + BITMAP "AAAAAAAA" + BITMAP "AAAAAAAA" + + BITMAP ".5....5." + BITMAP ".5....5." + BITMAP ".555555." + BITMAP ".5....5." + BITMAP ".5....5." + BITMAP ".555555." + BITMAP ".5....5." + BITMAP ".5....5." + + BITMAP "55555554" + BITMAP "54444454" + BITMAP "54555454" + BITMAP "54545454" + BITMAP "54555454" + BITMAP "54444454" + BITMAP "55555554" + BITMAP "44444444" + + BITMAP "5.555.5." + BITMAP "5.555.5." + BITMAP "5.555.5." + BITMAP "5.555.5." + BITMAP "5.555.5." + BITMAP "5.555.5." + BITMAP "5.555.5." + BITMAP "5.555.5." + + BITMAP "FFF.FFF." + BITMAP "........" + BITMAP "EEEEEEEE" + BITMAP "EEEEEEEE" + BITMAP "EEEEEEEE" + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP ".5....5." + BITMAP ".5....5." + BITMAP ".555555." + BITMAP ".5....5." + BITMAP ".5....5." + BITMAP ".555555." + BITMAP ".5....5." + BITMAP ".5....5." + + BITMAP "66686666" + BITMAP "66686666" + BITMAP "66686666" + BITMAP "88888888" + BITMAP "66666668" + BITMAP "66666668" + BITMAP "66666668" + BITMAP "88888888" + + BITMAP ".888888." + BITMAP ".888888." + BITMAP ".888888." + BITMAP "........" + BITMAP ".88.888." + BITMAP ".88.888." + BITMAP ".88.888." + BITMAP "........" + + BITMAP "........" + BITMAP "55555555" + BITMAP "55555555" + BITMAP "5.5.5.5." + BITMAP ".5...5.." + BITMAP "........" + BITMAP "........" + BITMAP "........" + + BITMAP ".5....5." + BITMAP ".5....5." + BITMAP ".555555." + BITMAP ".5....5." + BITMAP ".5....5." + BITMAP ".555555." + BITMAP ".5....5." + BITMAP ".5....5." + + BITMAP "666.666." + BITMAP "666.666." + BITMAP "666.666." + BITMAP "........" + BITMAP "666.666." + BITMAP "666.666." + BITMAP "666.666." + BITMAP "........" + + BITMAP ".A......" + BITMAP "..AA...." + BITMAP "....AA.." + BITMAP "......AA" + BITMAP "....AA.." + BITMAP "..AA...." + BITMAP ".A......" + BITMAP ".A......" + + BITMAP "........" + BITMAP "FFFFFFFF" + BITMAP "........" + BITMAP "5.5.5.5." + BITMAP ".5.5.5.5" + BITMAP "........" + BITMAP "FFFFFFFF" + BITMAP "........" + + BITMAP "E......E" + BITMAP "E......E" + BITMAP "EE....EE" + BITMAP "E.EEEE.E" + BITMAP "E......E" + BITMAP "E......E" + BITMAP "EE....EE" + BITMAP "E.EEEE.E" + + BITMAP "86666668" + BITMAP "68688666" + BITMAP "66886888" + BITMAP "68666888" + BITMAP "66888668" + BITMAP "66866888" + BITMAP "68668668" + BITMAP "66866888" + + BITMAP "C2CCC222" + BITMAP "C2CC2CC2" + BITMAP "CC2CC222" + BITMAP "C2CCCCCC" + BITMAP "CC2C2CCC" + BITMAP "CCC2C2C2" + BITMAP "CCC2CC2C" + BITMAP "CC2CC222" + + BITMAP "........" + BITMAP "6666666E" + BITMAP "6666666E" + BITMAP "EEEEEEEE" + BITMAP "666E6666" + BITMAP "666E6666" + BITMAP "EEEEEEEE" + BITMAP "........" + + BITMAP "....CC.." + BITMAP "....CC.." + BITMAP "...CC..." + BITMAP "...CC..." + BITMAP "..CC...." + BITMAP "..CC...." + BITMAP "...CC..." + BITMAP "...CC..." + + BITMAP ".F.F.F.." + BITMAP "FFFFFFF." + BITMAP ".F.F.F.." + BITMAP "FFFFFFF." + BITMAP ".F.F.F.." + BITMAP "FFFFFFF." + BITMAP ".F.F.F.." + BITMAP "........" + +game_sprites: + BITMAP ".......1.1.1...." + BITMAP ".....11A1A1A1..." + BITMAP "....1A1999991..." + BITMAP ".....1A999991..." + BITMAP "....1A9999191..." + BITMAP ".....1A969991..." + BITMAP "....1FF966691..." + BITMAP "..1FFFF19991...." + BITMAP ".1FF1FFF11111..." + BITMAP "1FF11FFFF1FFF1.." + BITMAP "1FFF1FFFF1FFF1.." + BITMAP "1FFF1F1111111..." + BITMAP ".11111FFFF111..." + BITMAP "...1FFF1FF7771.." + BITMAP "...177711F77771." + BITMAP "...17777111111.." + + BITMAP "......11A1A1A1.." + BITMAP ".....1A1999991.." + BITMAP "......1A999991.." + BITMAP ".....1A9999191.." + BITMAP "......19969991.." + BITMAP ".....1FF966691.." + BITMAP ".....1FF19991..." + BITMAP "....1FFFF111...." + BITMAP "....1FFFFF1....." + BITMAP "....1FF1FFF1...." + BITMAP "....1FF1FFF1...." + BITMAP "....1FFF111....." + BITMAP ".....1FFFF1....." + BITMAP ".....1FF1......." + BITMAP ".....177771....." + BITMAP ".....1777771...." + + BITMAP "....1.1.1......." + BITMAP "...1A1A1A11....." + BITMAP "...1999991A1...." + BITMAP "...199999A1....." + BITMAP "...1919999A1...." + BITMAP "...199969A1....." + BITMAP "...1966699F1...." + BITMAP "....19991FFFF1.." + BITMAP ".....111FFF1FF1." + BITMAP "..1FFF1FFFF11FF1" + BITMAP "..1FFF1FFFF1FFF1" + BITMAP "...111.111F1FFF1" + BITMAP "...111FFFF11111." + BITMAP "..1777FF1FFF1..." + BITMAP ".17777F117771..." + BITMAP "..11111177771..." + + BITMAP "..1A1A1A11......" + BITMAP "..1999991A1....." + BITMAP "..199999A1......" + BITMAP "..1919999A1....." + BITMAP "..199969A1......" + BITMAP "..1966699A1....." + BITMAP "...19991FF1....." + BITMAP "....111FFFF1...." + BITMAP ".....1FFFFF1...." + BITMAP "....1FFF1FF1...." + BITMAP "....1FFF1FF1...." + BITMAP ".....111FFF1...." + BITMAP ".....1FFFF1....." + BITMAP "......11FF1....." + BITMAP ".....177771....." + BITMAP "....1777771....." + + BITMAP "...1A1A1A1A1...." + BITMAP "....1AAAAA1....." + BITMAP "...1AAAAAAA1...." + BITMAP "...1AAAAAAA1111." + BITMAP "....1AAAAA11FFF1" + BITMAP "....1AAAAA11FFF1" + BITMAP ".....199911FF11." + BITMAP "...1FF111FFF1..." + BITMAP ".11FF1FFFF11...." + BITMAP "1FFF1.1111......" + BITMAP "1FFF11FFFF1....." + BITMAP ".111.1F11FF11..." + BITMAP "....1FF1.17771.." + BITMAP "...11FF1.177771." + BITMAP "..177771..1111.." + BITMAP ".1777771........" + + BITMAP "....1A1A1A1A1..." + BITMAP ".....1AAAAA1...." + BITMAP "....1AAAAAAA1..." + BITMAP ".1111AAAAAAA1..." + BITMAP "1FFF11AAAAA1...." + BITMAP "1FFF11AAAAA1...." + BITMAP ".11FF1199911...." + BITMAP "...1FFF111FF1..." + BITMAP "....11FFFF1FF11." + BITMAP "......1111.1FFF1" + BITMAP ".....1FFFF11FFF1" + BITMAP "...11FF11F1.111." + BITMAP "..17771.1FF1...." + BITMAP ".17771..1FF11..." + BITMAP "..111...177771.." + BITMAP "........1777771." + + BITMAP "..1331321......." + BITMAP ".13F33F21......." + BITMAP ".13F33F21......." + BITMAP ".1331321........" + BITMAP ".11333321......." + BITMAP "1633331321......" + BITMAP "6113313321....1." + BITMAP "...1113321...131" + BITMAP "...133321....131" + BITMAP "..1333321...1321" + BITMAP ".1333321....1321" + BITMAP ".133321.11.13321" + BITMAP ".13332113311321." + BITMAP ".13333333331321." + BITMAP "..1333333333321." + BITMAP "...12222112221.." + + BITMAP "....11.11......." + BITMAP "...1331321......" + BITMAP "..13F33F21......" + BITMAP "..13F33F21......" + BITMAP "..1331321......." + BITMAP "...1333321......" + BITMAP ".1133331321....." + BITMAP "16133313321....." + BITMAP "16611113321..1.." + BITMAP ".111333321..131." + BITMAP "..13333211.1321." + BITMAP "..13332132.1321." + BITMAP "..133321321321.." + BITMAP "..133333333321.." + BITMAP "...13333313321.." + BITMAP "....122211221..." + + BITMAP ".......1231331.." + BITMAP ".......12F33F31." + BITMAP ".......12F33F31." + BITMAP "........1231331." + BITMAP ".......12333311." + BITMAP "......1231333361" + BITMAP ".1....1233133116" + BITMAP "131...1233111..." + BITMAP "131....123331..." + BITMAP "1231...1233331.." + BITMAP "1231....1233331." + BITMAP "12331.11.123331." + BITMAP ".12311331123331." + BITMAP ".12313333333331." + BITMAP ".1233333333331.." + BITMAP "..12221122221..." + + BITMAP ".......11.11...." + BITMAP "......1231331..." + BITMAP "......12F33F31.." + BITMAP "......12F33F31.." + BITMAP ".......1231331.." + BITMAP "......1233331..." + BITMAP ".....123133331.." + BITMAP ".....12331333161" + BITMAP "..1..12331111661" + BITMAP ".131..1233331..." + BITMAP ".1231.11233331.." + BITMAP ".1231133123331.." + BITMAP "..123133123331.." + BITMAP "..123333333331.." + BITMAP "..12331333331..." + BITMAP "...122112221...." + +start_song: PROCEDURE + tick_note = 8 + song_note = 47 + END + +play_song: PROCEDURE + tick_note = tick_note + 1. + IF tick_note = 16. THEN + tick_note = 0. + song_note = song_note + 1. + IF song_note = 48. THEN song_note = 0. + note = song_notes(song_note) + SOUND 0, #note_freq(note - 1) + END IF + SOUND 0, , volume_effect(tick_note) + END + +sound_off: PROCEDURE + SOUND 0,,0 + SOUND 1,,0 + SOUND 2,,0 + SOUND 3,,0 + END + +volume_effect: + DATA BYTE 11,12,13,12,12,11,11,10 + DATA BYTE 10,9,9,10,10,9,9,8 + +song_notes: + DATA BYTE 1,2,3,4,5,4,3,2 + DATA BYTE 1,2,3,4,5,4,3,2 + DATA BYTE 6,4,7,8,9,8,7,4 + DATA BYTE 6,4,7,8,9,8,7,4 + DATA BYTE 3,12,8,10,11,10,8,12 + DATA BYTE 6,4,7,8,9,8,7,4 + +#note_freq: + DATA $01AC + DATA $0153 + DATA $011D + DATA $00FE + DATA $00F0 + DATA $0140 + DATA $00D6 + DATA $00BE + DATA $00B4 + DATA $00AA + DATA $00A0 + DATA $00E2 diff --git a/examples/vramcopy_sms.bas b/examples/vramcopy_sms.bas new file mode 100644 index 0000000..5ca61fc --- /dev/null +++ b/examples/vramcopy_sms.bas @@ -0,0 +1,47 @@ + ' + ' Example of DEFINE VRAM READ + ' + ' by Oscar Toledo G. + ' https://nanochess.org/ + ' + ' Creation date: Oct/15/2024. + ' Revision date: Apr/27/2025. Adapted for Sega Master System. + ' + + DIM array(128) + + CLS + MODE 4 + DEFINE CHAR 16,2,wall_bitmaps + + FOR #c = 0 TO 126 STEP 2 + VPOKE $3800 + #c, RANDOM(2) + 16 + NEXT #c + + DEFINE VRAM READ $3800,$0080,VARPTR array(0) + + FOR #c = 128 TO 1408 STEP 128 + DEFINE VRAM $3800 + #c, $0080, VARPTR array(0) + NEXT #c + + WHILE 1: WEND + +wall_bitmaps: + BITMAP "666E6666" + BITMAP "666E6666" + BITMAP "666E6666" + BITMAP "EEEEEEEE" + BITMAP "6666666E" + BITMAP "6666666E" + BITMAP "6666666E" + BITMAP "EEEEEEEE" + + BITMAP "FFFFFFF1" + BITMAP "F55F55FF" + BITMAP "F55F55FF" + BITMAP "F44F44FF" + BITMAP "F44F44FF" + BITMAP "F44F44FF" + BITMAP "FFFFFFFF" + BITMAP "1FFFFFFF" + diff --git a/gasm80 b/gasm80 deleted file mode 100755 index b5bdf50..0000000 Binary files a/gasm80 and /dev/null differ diff --git a/manual.txt b/manual.txt index 3667178..eec1d70 100644 --- a/manual.txt +++ b/manual.txt @@ -5,7 +5,7 @@ http://nanochess.org/ First developed: Feb/27/2024 -Last revision: Nov/12/2024. +Last revision: Jun/20/2026 >>>>>>>>>>>>>> Legal notice @@ -14,7 +14,7 @@ This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages or loss arising from the use of this software. -It is prohibited to modify, decompile, disassemble or reverse engineer this +It is prohibited to modify, decompile, disassemble or reverse engineer this software. All trademarks are property of their respective owners. @@ -22,8 +22,70 @@ All trademarks are property of their respective owners. >>>>>>>>>>>>>> Changes in this version -v0.8.0 Nov/12/2024 o Added support for VDP.STATUS (for reading VDP status) - o Added SELECT CASE / END CASE +v0.9.2 Jul/23/2026 o FM synthesis for MSX, MSX2, and Sega Master System. + o Added OPTION FM. + o Added PLAY INSTRUMENT. + o Added PLAY FM. + o Added MUSIC.FM_ENABLED and MUSIC.FM_AVAILABLE. + o Added C-style preprocessor (contributed by visrealm) + o Allows using PALETTE on MSX games, but only applies + on MSX2. + o Z80: Optimization in multiple array access in + expressions. + o Z80: Avoid constant reload. + o Z80: Optimization IF with 8-bit AND, and + multiplication by 128. + o Z80: Speed up of block copy to screen and VRAM fill. + o Z80: Optimized 16-bit multiplication (before always + 1277 cycles, now minimum of 191 cycles and maximum + 1244 cycles) + o SMS: Solved bug reading buttons for 2nd controller. + o Colecovision: Solved bug of faster frame rate when + using spinners. + o Added example vgm_nes.bas + +v0.9.1 Feb/17/2026 o Added support for MSX2 using --msx2 + o MSX2: It supports the PALETTE statement. + o MSX2: It supports MODE 4, extended sprites, + DEFINE SPRITE COLOR, BORDER, and SCROLL. + o MSX2/SMS: Added PALETTE DEFAULT. + o MSX2: Access to 64K of VRAM using VPOKE/VPEEK. + o Added note about support of Blue and Purple buttons + (Super Action Controllers for Colecovision) + o Added support for spinners and roller controller + using CONT1.SPINNER and CONT2.SPINNER (Colecovision) + o NES: Solved important bug where VPOKE/PRINT would + corrupt some variables, and arrays could be + allocated to a wrong address. + +v0.9.0 Aug/26/2025 o Added support for NES/Famicom using --nes + o MSX: Added support for Konami mapper using -konami + o Solved bug in DEF FN precedence inside expressions. + o Solved bug in > operator with 16-bit variable. + o NABU: Solved bug where compilation would warn of both + sound chips. + o TI-99/4A: Corrected bug in CHR$ and DEFINE VRAM READ. + o TI-99/4A: Solved bugs doing operations with 8-bit + variables and 16-bit values. + o TI-99/4A: Solved bug in multiplication by 1. + o TI-99/4A: Solved bug in SELECT CASE. + o TI-99/4A: Solved bug in DEFINE COLOR PLETTER. + o TI-99/4A: Optimizes more operations with constants. + o TI-99/4A: Added more optimization using register + labeling, saves many bytes, and speeds up. + +v0.8.0 Apr/26/2025 o Added support for Sega Master System using --sms + o Added support for VDP.STATUS (for reading VDP status) + o Added SELECT CASE / END SELECT + o Added support for CHR$ in PRINT (for portable printing + of arbitrary characters in the current cursor position) + o Added SCROLL statement for Sega Master System. + o Added BORDER flags for Sega Master System. + o Added PALETTE statement for Sega Master System. + o Adapted BITMAP, SCREEN, and SPRITE for Sega Master + System. + o Optimizes code using directly DI/EI instructions in + Z80 platforms if possible. v0.7.1 Oct/15/2024 o Added support for NABU PC using --nabu (with -cpm option) o Added support for USR/CALL with up to 5 arguments (only Z80) @@ -73,7 +135,9 @@ v0.5.1 Jun/25/2024 o Added SIGNED and UNSIGNED keywords to tag variables >>>>>>>>>>>>>>. Description -CVBasic is an integer BASIC compiler for Colecovision, MSX, Sega SG1000, Spectravideo SVI-318/328, Sord M5, Memotech MTX, and Creativision. It works as a cross-compiler over a PC and generates assembler code that can be processed by Gasm80. +CVBasic is an integer BASIC compiler for Colecovision, MSX, Sega SG1000, Spectravideo SVI-318/328, Sord M5, Memotech MTX, Creativision, Soundic Pencil II, Tatung Einstein, Casio PV-2000, TI-99/4A, NABU PC, Sega Master System, and NES/Famicom. + +It works as a cross-compiler over a PC and generates assembler code that can be processed by Gasm80. Platforms requirements: o PC with Windows XP or better. @@ -98,8 +162,11 @@ Usage: cvbasic --creativision in.bas output.asm cvbasic --pencil in.bas output.asm cvbasic --einstein in.bas output.asm + cvbasic --pv2000 in.bas output.asm cvbasic --ti994a in.bas output.a99 cvbasic --nabu in.bas output.asm + cvbasic --sms in.bas output.asm + cvbasic --nes in.bas output.asm The following modules are automatically included as the prologue and epilogue of your generated code and they set important variables and helper code: @@ -116,11 +183,22 @@ The Creativision port uses cvbasic_6502_prologue.asm and cvbasic_6502_epilogue.a The TI-99/4A port uses cvbasic_9900_prologue.asm and cvbasic_9900_epilogue.asm and it should be assembled using xdt tools. (xas99 with the -R option) +The NES/Famicom port uses cvbasic_nes_prologue.asm and cvbasic_nes_epilogue.asm + The NABU port requires the gasm output file to be named 000001.nabu and you should ZIP it to test it with MAME. If you use the -cpm option you can use any .com file name. And finally, you can test the generated output.rom file using CoolCV, blueMSX, openMSX, or a Flash cartridge for real hardware. +>>>>>>>>>>>>>> Debugging support + +If you are using the blueMSX or openMSX emulators, you can debug your games with symbols intermixed in the code disassembly window. + +For this you require gasm80 to generate a symbol table in this way: + + gasm80 game.asm -o game.rom -s game.sym + + >>>>>>>>>>>>>> Supporting the developer If you find CVBasic useful, please show your appreciation making a donation via Paypal ($9 USD suggested) to b-i+y-u+b-i (at) gmail.com @@ -193,6 +271,11 @@ The following statements are available: Constants that start with the # prefix are treated as 16-bit numbers, and if this isn't present then the constant will be treated as an 8-bit number. + + Constants can be passed-in to CVBasic via the command-line: + + cvbasic -DMYCONST=123 in.bas output.asm + cvbasic -D#MYBIGCONST=12345 in.bas output.asm GOTO label @@ -353,7 +436,7 @@ The following statements are available: [statement] CASE ELSE [statement] - END CASE + END SELECT It evaluates the expression, and selects the statements to execute from the CASE statements. CASE ELSE should be the last statement if it is required to @@ -384,6 +467,55 @@ The following statements are available: Poke VRAM with data. + For Sega Master System: + + All the VRAM $0000-$3fff can be used for tiles. Each 8x8 tile uses 32 bytes. + Each 8 bytes cover a color bit from least significant bit to most significant + bit. + + However the area $3800-$3dff contains the screen pattern data. The even bytes + contain the pattern number, and the odd bytes contain the bit 8 of pattern + number. See the SCREEN statement for further details on the flags. + + For NES/Famicom: + + Only the VRAM area $2000-$27ff can be accessed. By default the full screen is + displayed from the area $2000-$23ff. The remaining is used when the SCROLL + statement is used. + + Any write to VRAM (VPOKE, PRINT, SCREEN) is buffered, this means it doesn't + happen immediately, but instead when the next vertical refresh happens on + the screen. This also means there is a limit to how much data you can + update in a single frame (typically around 96-128 bytes). + + The internal buffer can get filled (22 VPOKE or number digits from PRINT, + 12 strings PRINT, or 12 SCREEN lines), and it will trigger automatically a + WAIT so it doesn't overflow. + + If you are writing more than 96-128 bytes per frame, you can use + SCREEN DISABLE and SCREEN ENABLE. + + These are the palette assignment bytes for the main screen. + + $23c0 = XX XX XX XX XX XX XX XX + $23c8 = XX XX XX XX XX XX XX XX + $23d0 = XX XX XX XX XX XX XX XX + $23d8 = XX XX XX XX XX XX XX XX + $23e0 = XX XX XX XX XX XX XX XX + $23e8 = XX XX XX XX XX XX XX XX + $23f0 = XX XX XX XX XX XX XX XX + $23f8 = XX XX XX XX XX XX XX XX + + Where each byte covers a 4x4 area: + + 2 cols 2 cols + |-------|-------| + |bit 1-0|bit 3-2| 2 rows + |-------|-------| + |bit 5-4|bit 7-6| 2 rows + |-------|-------| + + WAIT Waits for the next frame interruption (1/60 of a second for NTSC or 1/50 @@ -466,7 +598,7 @@ The following statements are available: DEFINE CHAR char_num,total,VARPTR array(expr) DEFINE CHAR PLETTER char_num,total,label - Loads graphics into VRAM. + Loads graphics into VRAM. This sentence isn't used on NES/Famicom. "char_num" valid values are between 0 and 255. @@ -477,7 +609,9 @@ The following statements are available: BITMAP statements or DATA BYTE statements. Each character is an 8x8 pixel bitmap (represented as 8 BITMAP statements or - 8 constants in a DATA BYTE statement). + 8 constants in a DATA BYTE statement). For Sega Master System, this is 32 + constants in a DATA BYTE statement because the character definition includes + color. Internally, CVBasic will replicate the data along the 3 display areas so the defined characters will appear on the whole screen when required. @@ -489,11 +623,14 @@ The following statements are available: with the Pletter compressor (TMSColor does the compression if you give the option -z) + This sentence isn't supported for NES/Famicom. + DEFINE COLOR char_num,total,label DEFINE COLOR char_num,total,VARPTR array(expr) DEFINE COLOR PLETTER char_num,total,label - Loads color into VRAM. + Loads color into VRAM. This sentence isn't used on Sega Master System nor + NES/Famicom. "char_num" valid values are between 0 and 255. @@ -521,17 +658,17 @@ The following statements are available: DEFINE SPRITE sprite_num,total,VARPTR array(expr) DEFINE SPRITE PLETTER sprite_num,total,label - Loads sprites into VRAM + Loads sprites into VRAM. This sentence isn't used on NES/Famicom. - "sprite_num" valid values are between 0 and 63. + "sprite_num" valid values are between 0 and 63 (127 for Sega Master System) - "total" valid values are between 1 and 64. + "total" valid values are between 1 and 64 (128 for Sega Master System) It replaces the definitions for "sprite_num", "total" means the total number of sprites to load, and "label" should point to a label containing BITMAP or DATA BYTE statements with the sprite or sprites representation. - Each sprite is 16x16 pixels. + Each sprite is 16x16 pixels (8x16 pixels for Sega Master System) The VARPTR expression allows sprite definition from an offset inside an array so you can do animation easily. @@ -540,12 +677,31 @@ The following statements are available: with the Pletter compressor (TMSColor does the compression if you give the option -z) + DEFINE SPRITE COLOR sprite_num,total,label + DEFINE SPRITE COLOR sprite_num,total,VARPTR array(expr) + + This is for MSX2 sprites (MODE 4) + + It allows to define the color for each pixel row of a 16x16 sprite. + + The data should be represented as DATA BYTE and 16 values for each sprite. + + Each byte contains the color (bits 3-0) + + bit 5 = IC bit. The sprite with this bit set doesn't generate a + collision. + bit 6 = CC bit. The sprite with this bit set has its pixels OR'ed + with the immediately preceding sprite with CC bit 0 (useful + to create more colors) + bit 7 = EC bit, When set the sprite is rendered 32 pixels to the + left. + DEFINE VRAM address,length,label DEFINE VRAM address,length,VARPTR array(expr) DEFINE VRAM PLETTER address,length,label - Copies data from "label" into VRAM address "address". - The total of bytes is defined by "length". + Copies data from "label" into VRAM address "address". The total of bytes + is defined by "length". This sentence isn't supported for NES/Famicom. The VARPTR expression allows choosing the origin from an offset inside an array so you can do animation easily. @@ -557,8 +713,8 @@ The following statements are available: DEFINE VRAM READ address,length,label DEFINE VRAM READ address,length,VARPTR array(expr) - Copies data from VRAM address "address" into "label". - The total of bytes is defined by "length". + Copies data from VRAM address "address" into "label". The total of bytes + is defined by "length". This sentence isn't supported for NES/Famicom. The VARPTR expression allows choosing the origin from an offset inside an array. @@ -609,7 +765,46 @@ The following statements are available: NOTE: USE ONLY VALUES $80-$BF FOR MIX BECAUSE ANY VALUE OUTSIDE THIS CAN BURN YOUR MSX HARDWARE. - SPRITE index,y,x,f,c + SOUND 10,[freq 16 bits],[vol 8 bits],[sweep 8 bits] + SOUND 11,[freq 16 bits],[vol 8 bits],[sweep 8 bits] + SOUND 12,[freq 16 bits],[linear counter] + SOUND 14,[freq 16 bits],[vol 8 bits] + SOUND 15,[setting 8 bits] + + Allows to generate tones on the NES/Famicom Audio Processing Unit (APU). + + For SOUND 10, 11, and 12, the frequency is a 11-bit number, and it has a + duration. For starters you can add $0800 so you can make sure the tone + will have the maximum duration (127 frames). + + For SOUND 14 the frequency is a 4-bit number, also you need to add $0800. + + The volume is a number from zero (silent) to fifteen (highest volume). + + Bit 4 means the volume is fixed (you probably will always use this by + adding $10), else it uses an envelope decay (in combination with bit 5) + + Bits 7-6 indicate the duty cycle (0= 12.5%, 1= 25%, 2= 50%, 3= 75%) + + Sweep + bit 7: 0=disable, 1=enable + bit 6-4: Sweep update rate NTSC = 120hz / (n + 1), PAL = 96 hz / (n + 1) + bit 3: 0=+increase, 1=-decrease + bit 2-0: Sweep right shift amount (1 means one bit to right) + until frequency value becomes zero. + + As SOUND 12 doesn't have a volume register, you should turn it on using + SOUND 15, 15, and turn it off using SOUND 15, 11. Also the linear counter + should have a non-zero value. + + Don't update the frequency if it is the same, or otherwise the output + phase will reset and generate glitches in the audio output. + + + SPRITE index,y,x,f,c (All others) + SPRITE index,y,x,f (SMS) + SPRITE index,y,x,f (MSX2) + SPRITE index,y,x,f,a (NES/Famicom) The first parameter indicates the number of the sprite (0-31). @@ -620,12 +815,21 @@ The following statements are available: X contains the X-coordinate for the sprite (0-255) F contains the sprite frame. You must multiply the desired sprite - definition (0-63) by 4 to show the right sprite. + definition (0-63) by 4 to show the right sprite. For Sega Master + System the sprite frame is based on 8x8 blocks, so you need to use + multiplies of 2 for each 8x16 sprite (0-254) C contains the color for the sprite (0-15). Alternatively, you can add the flag $80 (128 decimal) to displace the sprite by 32 pixels to the left. Allowing it to blend inside the screen from the left. + The fourth argument (color) isn't used on Sega Master System, and + it will generate an error from CVBasic if present. + + For MSX2, the fourth argument is optional if MODE 4 has been + selected, and the color for the sprites should be defined using + the statement DEFINE COLOR SPRITE. + Sprites are updated on the vertical retrace. The Y-coordinate visible range for sprites is 240 to 255, and 0 @@ -636,7 +840,23 @@ The following statements are available: Additionally, the value 208 for the Y-coordinate disables sprite processing starting on that very sprite. For example, - SPRITE 0,$d0,0,0,0 forces sprites 1-31 to disappear even if active. + SPRITE 0,$d0,0,0,0 forces sprites 1-31 to disappear even if active + (For MSX2 the value disabling sprite processing is 216 or $d8) + + NES/Famicom notes: + + y = Vertical position - 1 ($ff, $00 - $ee = scanline 0..239) + To hide sprites use $f0 ($ef - $fe aren't displayed) + x = Horizontal position. + f = bit 7-1 = pattern number, bit 0 = pattern address ($0000 or $1000) + An address of $0000 means the sprite will access the + patterns 0-254, and an address of $1000 means the sprite + will access the patterns 256-510. + a = bit 7 = 1 = Vertical flip. + bit 6 = 1 = Horizontal flip. + bit 5 = 1 = Sprite behind background. + bit 4-2 = Reserved. + bit 1-0 = Sprite palette (0-3) SPRITE FLICKER ON SPRITE FLICKER OFF @@ -650,6 +870,8 @@ The following statements are available: borders, overlapping sprites in different depths, or using the SPRITE 0,$d0,0,0,0 trick. + Sprite flicker doesn't work when using MODE 4 in MSX2. + OUT port,data It outputs "data" in the hardware port "port". Not available in 6502 targets. @@ -683,6 +905,10 @@ The following statements are available: PRINT AT 736,"A" ' Bottom-left corner PRINT AT 767,"A" ' Bottom-right corner + NES/Famicom allows for a 32x30 screen area (960 characters), + however, only the center area (28 rows) can be guaranteed + to be visible. + PRINT expr PRINT expr ' Right-aligned with zeroes to 'const' size. PRINT <.const>expr ' Right-aligned with spaces to 'const' size. @@ -693,10 +919,116 @@ The following statements are available: PRINT AT 8,"Score:",<5>#score,"00" + PRINT CHR$(expr) + + Displays expr as a character on the video screen. + + Example: + + PRINT CHR$(65) + BORDER color + BORDER color,flags Select the border color. It can be a number from 0 to 15. + The first argument isn't available for NES/Famicom, the small border around + is always black. + + The flags argument for Sega Master System. + bit 0 = Set column 0 to border color. + bit 1 = Horizontal scrolling disabled on rows 0-1. + bit 2 = Vertical scrolling disabled on columns 24-31. + + The flags argument for NES/Famicom. + bit 0 = Background clipping (0 = hide left 8-pixel column, 1 = no clipping) + bit 1 = Sprite clipping (0 = hide left 8-pixel column, 1 = no clipping) + + The flags argument for MSX2+ (not MSX2) + bit 0 = Extend screen internal width to 64 characters. + bit 1 = Hide column 0. + + SCROLL x, y + + Sets the pixel scrolling for Sega Master System background. + + Sets the pixel scrolling for NES/Famicom background. + + Sets the pixel scrolling for MSX2 (the x-coordinate only in MSX2+) + + PALETTE register, color + PALETTE LOAD array_data_byte + PALETTE LOAD VARPTR array_data_byte(0) + PALETTE DEFAULT + + For MSX/MSX2: + + Using this in --msx mode won't have effects. It is for allowing MSX1 games + to look better in MSX2. + + Sets a single palette register (0-15) to the color. Color bits are + specified in a word as 0000 0GGG 0RRR 0BBB (three bits each for + red, green, and blue) + + Alternatively, you can load a full 16 entries palette using PALETTE LOAD. + The array should be specified with DATA. + + You can restore the palette to TMS9118 colors by using PALETTE DEFAULT. + + For the Sega Master System: + + Sets a single palette register (0-31) to the color (0-63). Color bits are + specified in a single byte as 00BBGGRR (two bits each for red, green, and + blue). + + The registers 0-15 are for background tiles, and the registers 16-31 are + for sprites. The tiles can request to use the sprite colors. + + Alternatively, you can load a full 32 entries palette using PALETTE LOAD. + The array should be specified with DATA BYTE. + + You can restore the palette to TMS9118 colors by using PALETTE DEFAULT. + + It is recommended to update the palette only in the vertical blanking + to avoid glitches on the display. + + For the NES/Famicom: + + Registers + 0-3 palette 0 for background tiles. + 4-7 palette 1 for background tiles. + 8-11 palette 2 for background tiles. + 12-15 palette 3 for background tiles. + 16-19 palette 0 for sprites + 20-23 palette 1 for sprites + 24-27 palette 2 for sprites + 28-31 palette 3 for sprites + + The value for each entry is in the range of 0-63 (the lower 4 bits choose + the color, and bits 5-4 choose the luminance). The black color is any of + the values $0e, $0f, $1e, $1f, $2e, $2f, $3e, or $3f. Don't use the value + $0d as it generates a blacker than black signal that may cause unstable + picture. + + Color table: + + 0= White. 8= Lemon + 1= Dark blue. 9= Grass + 2= Blue. A= Green + 3= Magenta. B= Pear + 4= Purple. C= Turquoise + 5= Wine. D= Gray + 6= Red. E= Black + 7= Golden. F= Black + + For a reference of the palette colors see + https://www.nesdev.org/wiki/PPU_palettes#2C02 + + When you update the full palette the 32 bytes are written into the palette + memory, and because how the NES/Famicom palette memory is implemented, + the value at the offset 16 will be written over the offset 0 (the background + color) + MODE 0 Select the video mode 0. In this mode, the 32x24 screen grid can have one @@ -715,6 +1047,8 @@ The following statements are available: 6 - Dark red. e - Gray. 7 - Cyan. f - White. + For NES/Famicom, it simply clears the screen and erases the sprites. + MODE 1 Select the video mode 1. This is a high-resolution video mode where @@ -732,6 +1066,18 @@ The following statements are available: VRAM addresses $2000-$201f contain the color for the characters group. + MODE 4 + + Sega Master System: It is the only mode supported. VRAM $0000-$1fff are + setup as background bitmaps, $2000-$37ff for sprites bitmaps, $3800-$3dff + for the background patterns, and $3f00-$3fff for the sprites attributes. + The unused portion $3e00-$3eff and $3f40-$3f7f can be used for extra sprites + (sprite numbers 120-123 and 125) + + MSX2: It is pretty similar to MODE 0, except that extended sprites are + enabled. This allows for eight sprites in a row. And each sprite can have + a different color per row. The colors for the sprites should be defined + using the statement DEFINE SPRITE COLOR. SCREEN DISABLE @@ -739,6 +1085,9 @@ The following statements are available: screen (can be set up using the BORDER statement). The video can be enabled again with SCREEN ENABLE or using a MODE statement. + For NES/Famicom add a WAIT statement after this one because the operation + is executed on the next video frame. + SCREEN ENABLE Enables the video output (preserves the previous video mode set by MODE). @@ -773,6 +1122,49 @@ The following statements are available: SCREEN tile,0,0,2,2 + For the Sega Master System the source offset, target offset, and width are + automatically multiplied by 2, because each character in the screen uses + two bytes in VRAM. The first byte referring to the tile number (0-255), and + the second byte specifies how it is used: + + bit 0 - Indicates to use tiles in the range 256-511 + bit 1 - Horizontal flip if set. + bit 2 - Vertical flip if set. + bit 3 - Uses sprite palette if set. + bit 4 - The tile will be drawn on top of sprites. + + NAMETABLE number + + For NES/Famicom it selects the type of nametable arrangement. This is an + informative field for emulators. + + 0 - Vertical scrolling (CIRAM A10 = PPU A11) + 1 - Horizontal scrolling (CIRAM A10 = PPU A10) + 2 - Single-screen board (only with bank-switching) + 3 - 4-screen board (only with bank-switching) + + CHRRAM number + + For NES/Famicom bank-switching mode it allows to select the CHRRAM bank (0-3) + + The bit 2 (value 4) allows to select the PPU screen mirroring (0 for vertical + scrolling, 1 for horizontal scrolling) See the UNROM 512 documentation. + + CHRROM number + CHRROM number PATTERN number + CHRROM PATTERN number + + For NES/Famicom the graphic data are saved into CHRROM banks of 8K. + + You should specify the CHRROM bank before putting any BITMAP statement. + Currently only CHRROM 0 is supported for 32K ROM, and 0-3 for bank-switching + ROM (UNROM 512) + + CVBasic will predefine an ASCII charset with color 3 in the patterns 32-127. + + You can choose the pattern number (0-511) for the following BITMAP statements. + + BITMAP "00000000" BITMAP "00001111" @@ -793,6 +1185,14 @@ The following statements are available: Every other character will be interpreted as one. + For the Sega Master System, the sprites are composed by 8x16 pixels. If you + use a 16x16 bitmap, it is automatically divided into 2 sprites of 8x16 pixels. + The colors for each pixel are specified by using hexadecimal digits 1 to 9, + and A to F. + + For the NES/Famicom, the sprites are composed by 8x16 pixels. The colors for + each pixel are specified by the digits 1 to 3. + PLAY SIMPLE PLAY SIMPLE NO DRUMS PLAY FULL @@ -865,7 +1265,7 @@ The following statements are available: is not specified thereafter. Notice that the instrument waveforms can be altered - by modifying the cvbasic_epilogue.asm file for your + by modifying the cvbasic_prologue.asm file for your program. Notice the bass instrument reduces two octaves your @@ -931,6 +1331,8 @@ The following statements are available: Sets the VDP register to the expression value. + Not supported for NES/Famicom. + CALL NAME Calls assembly function NAME without arguments. Note CVBasic will put the @@ -956,8 +1358,8 @@ The following statements are available: BANK ROM 1024 This statement allows you to use 128K-1024K cartridges for your program - (Opcode's Megacart for the Colecovision, ASCII 16K mapper for MSX, and - SMS Sega Mapper for SG1000). + (Opcode's Megacart for the Colecovision, ASCII 16K mapper for MSX, SMS + Sega Mapper for SG1000, and UNROM 512 for NES/Famicom). It should be the very first statement in your program. 128 allows for 8 banks, 256 allows for 16 banks, 512 allows for 32 banks, and 1024 allows @@ -969,6 +1371,10 @@ The following statements are available: In bank 0 you should have global procedures and data that are shared by all your banks. + Note for NES/Famicom, you cannot refer to the top three banks (banks 13-15 + for 256K, and banks 29-31 for 512K) as these are used to save the CHRROM + data. + BANK 1-63 Select the bank for the following code/data of your program. Each bank can @@ -1028,7 +1434,7 @@ The expression parser will take advantage of 8-bit variables and numbers to opti an expression. A=VPEEK(expr) Reads a VRAM memory location VPEEK always reads 8-bit data that can be processed in - an expression. + an expression. Not supported for NES/Famicom. A=ABS(expr) Gets the absolute value of the expression (non-negative) A=SGN(expr) Gets sign of expression (-1, 0 or 1) A=array(expr) Accesses an array. The array can be defined with DIM or @@ -1057,8 +1463,9 @@ The expression parser will take advantage of 8-bit variables and numbers to opti CONT.RIGHT Non-zero if any controller pointing right CONT.BUTTON Non-zero if any controller left button is pressed. CONT.BUTTON2 Non-zero if any controller right button is pressed. - CONT.KEY Current pressed key in any controller (0-9 for numbers, - 10-*, 11-#, 15-Not pressed) + CONT.KEY Current pressed key in any controller: 0-9 for numbers, + 10-*, 11-#, 15-Not pressed. For Super Action Controllers, + 12 is the Blue button, and 13 is the Purple button. CONT1 Contains the state of the first controller. CONT1.UP Non-zero if controller pointing up @@ -1067,7 +1474,13 @@ The expression parser will take advantage of 8-bit variables and numbers to opti CONT1.RIGHT Non-zero if controller pointing right CONT1.BUTTON Non-zero if the controller left button is pressed. CONT1.BUTTON2 Non-zero if the controller right button is pressed. - CONT1.KEY Current pressed key (0-9 for numbers, 10-*, 11-#, 15-Not pressed) + CONT1.KEY Current pressed key: 0-9 for numbers, 10-*, 11-#, 15-Not pressed. + For Super Action Controllers, 12 is the Blue button, + and 13 is the Purple button. + CONT1.SPINNER Reads the spinner displacement for this controller, + and resets the count (save in a variable if required). + This is the X displacement for the Roller Controller. + Only supported for Colecovision. CONT2 Contains the state of the second controller. CONT2.UP Non-zero if controller pointing up @@ -1076,7 +1489,13 @@ The expression parser will take advantage of 8-bit variables and numbers to opti CONT2.RIGHT Non-zero if controller pointing right CONT2.BUTTON Non-zero if the controller left button is pressed. CONT2.BUTTON2 Non-zero if the controller right button is pressed. - CONT2.KEY Current pressed key (0-9 for numbers, 10-*, 11-#, 15-Not pressed) + CONT2.KEY Current pressed key: 0-9 for numbers, 10-*, 11-#, 15-Not pressed. + For Super Action Controllers, 12 is the Blue button, + and 13 is the Purple button. + CONT2.SPINNER Reads the spinner displacement for this controller, + and resets the count (save in a variable if required). + This is the Y displacement for the Roller Controller. + Only supported for Colecovision. RANDOM Produces a pseudo-random value between 0 and 65535. @@ -1113,18 +1532,143 @@ The expression parser will take advantage of 8-bit variables and numbers to opti MUSIC.PLAYING Returns zero if music is not currently playing, returns a non-zero value otherwise. + +>>>>>>>>>>>>>> Preprocessor + +The preprocessor can be used to conditionaly include or exclude +CVBasic code from being compiled. This can be useful for including +platform-dependent code or to enable and disable diagnostics +displays. + + #IF [NOT] constant + [statement] + [statement] + #ELSE + [statement] + [statement] + #ENDIF + +Unlike regular IF-THEN-ELSE statements, the excluded code is not +compiled at all so it won't take up additional space in your compiled +rom image. + +One of the following pre-defined constants will be available and +set to equal 1 depending on the build target chosen: + + COLECOVISION, SG1000, MSX, SGM, SVI, SORD, MEMOTECH, CREATIVISION, + PENCIL, EINSTEIN, PV2000, TI994A, NABU, SMS + +These can be used to include or exclude CVBasic code for a given +target platform. eg: + + #IF TI994A + CONST BANKSIZE=8 + #ELSE + CONST BANKSIZE=16 + #ENDIF + +Compile-time logging: + + #INFO string + Output an information message at compile time. + + #WARNING string + Output a warning message at compile time. + + #ERROR string + Output an error message and halt compilation at compile time. + + +>>>>>>>>>>>>>>. FM support for MSX/MSX2/SMS + +CVBasic supports FM synthesis for MSX/MSX2 computers, and Sega Master System consoles +with FM card. + +For MSX and MSX2 computers it is needed an external FM cartridge. For MSX2+ computers +the FM support is built-in. + +Any game using FM support must use bank-switching. + +The FM library code will be automatically included in the bank 7, and 768 bytes of +space will be subtracted of the 16K. + +An example is available in the directory examples/music_fm.bas + + OPTION FM ON + This enables the FM support for your game (Yamaha YM2413) + + With FM support, CVBasic will check automatically at startup for the FM chip, + and if detected it will enable it automatically for music playing (default + instruments are prepared) + + If no FM is detected, CVBasic will fall gracefully back to the SN76489 or + AY-3-8910 music playing. + + MUSIC.FM_AVAILABLE + This indicates with zero if FM isn't available, or non-zero if it is available. + + MUSIC.FM_ENABLED + This indicates with non-zero if the music player is using FM to play music, or + zero if it is playing music using the AY-3-8910 chip (or SN76489 in SMS) + + PLAY FM ON + PLAY FM OFF + This enables or disables the FM support. Useful for making your game to switch + FM support to PSG and vice-versa. + + Turning on FM without the chip won't cause any effect, and the music playing + will be still through the PSG chip. + + PLAY INSTRUMENT voice1,voice2,voice3,voice4 + This selects the FM instruments that will be used for the corresponding + instruments of the music player (piano, clarinet, flute, and bass). + + It should be used before playing a song. + + Bits 3-0 contain the volume for the instrument (0 being the maximum volume, + and 15 almost silent). + + Bits 7-4 contains the instrument number: + + 0 - Programmable (see PLAY INSTRUMENT VARPTR) + 1 - Violin + 2 - Guitar + 3 - Piano + 4 - Flute + 5 - Clarinet + 6 - Oboe + 7 - Trumpet + 8 - Organ + 9 - Horn + a - Synthesizer + b - Harpsichord + c - Vibraphone + d - Synthesizer bass + e - Wood bass + f - Electric guitar + PLAY INSTRUMENT VARPTR fm_data(0) + This loads the only programmable instrument. + + For example: + + fm_data: + DATA BYTE $31,$11,$0e,$20,$d9,$b2,$11,$f4 ' Piano 1 + + >>>>>>>>>>>>>> Useful VRAM addresses Currently CVBasic handles in its own the bitmap, color and sprites tables. However, you probably will need direct access to the screen. -The screen is a 32x24 grid located at the VRAM address $1800-$1aff. It can be accessed using VPOKE (writing) and VPEEK (reading). +The screen is a 32x24 grid located at the VRAM addresses $1800-$1aff. It can be accessed using VPOKE (writing) and VPEEK (reading). See the included example viboritas.bas for examples. +For Sega Master System the screen is a 32x28 grid located at the VRAM addresses $3800-$3dff. It can be accessed using VPOKE and VPEEK. SMS uses two bytes for each pattern to display on the screen, the first byte is the pattern number, and the second byte are flags. See the SCREEN statement for further details on the flags. + >>>>>>>>>>>>>> Assembly language interfacing @@ -1212,7 +1756,15 @@ CoolCV emulator https://forums.atariage.com/topic/240800-coolcv-emulator-for-mac-os-x-linux-windows-and-raspberry/ +Amy's CVPaint Studio + + https://amypurple.github.io/AmysCVPaintStudio/ + +Kamshaft's CV Toolkit, including Sprite Tool, Tile Creator, Sound Creator, and Music Tracker + + https://cvaddict.com/tools/ + >>>>>>>>>>>>>> Acknowledgments Thanks to following members of Atariage for contributing valuable suggestions: diff --git a/node.c b/node.c index a8bf147..2f67497 100644 --- a/node.c +++ b/node.c @@ -94,7 +94,7 @@ static char *node_types[] = { "N_READ8", "N_READ16", "N_NUM8", "N_NUM16", "N_PEEK8", "N_PEEK16", "N_VPEEK", "N_INP", "N_ABS16", "N_SGN16", - "N_JOY1", "N_JOY2", "N_KEY1", "N_KEY2", + "N_JOY1", "N_JOY2", "N_KEY1", "N_KEY2", "N_SPINNER1", "N_SPINNER2", "N_RANDOM", "N_FRAME", "N_MUSIC", "N_NTSC", "N_POS", "N_VDPSTATUS", "N_ADDR", "N_USR", "N_COMMA", @@ -373,10 +373,15 @@ struct node *node_create(enum node_type type, int value, struct node *left, stru /* ** Optimize a 16-bit variable read to a 8-bit variable read + ** + ** This optimization isn't possible with TMS9900 because + ** byte read gets data from the high byte. */ - if (left->type == N_LOAD16) { - left->type = N_LOAD8; - return left; + if (target != CPU_9900) { + if (left->type == N_LOAD16) { + left->type = N_LOAD8; + return left; + } } /* @@ -786,16 +791,18 @@ struct node *node_create(enum node_type type, int value, struct node *left, stru ** parenthesis: 1 - open and close () 2 - # - 3 - open only ( + 3 - #( 4 - @ */ void node_get_label(struct node *node, int parenthesis) { temp[0] = '\0'; - if (parenthesis == 1 || parenthesis == 3) + if (parenthesis == 1) strcat(temp, "("); else if (parenthesis == 2) strcat(temp, "#"); + else if (parenthesis == 3) + strcat(temp, "#("); else if (parenthesis == 4) strcat(temp, "@"); if (node->label->length) { diff --git a/node.h b/node.h index 71c6622..7e27ee2 100644 --- a/node.h +++ b/node.h @@ -29,28 +29,12 @@ enum node_type { N_READ8, N_READ16, N_NUM8, N_NUM16, N_PEEK8, N_PEEK16, N_VPEEK, N_INP, N_ABS16, N_SGN16, - N_JOY1, N_JOY2, N_KEY1, N_KEY2, + N_JOY1, N_JOY2, N_KEY1, N_KEY2, N_SPINNER1, N_SPINNER2, N_RANDOM, N_FRAME, N_MUSIC, N_NTSC, N_POS, N_VDPSTATUS, N_ADDR, N_USR, N_COMMA, }; -#define REG_A 0x01 -#define REG_F 0x02 -#define REG_B 0x04 -#define REG_C 0x08 -#define REG_D 0x10 -#define REG_E 0x20 -#define REG_H 0x40 -#define REG_L 0x80 - -#define REG_AF (REG_A | REG_F) -#define REG_BC (REG_B | REG_C) -#define REG_DE (REG_D | REG_E) -#define REG_HL (REG_H | REG_L) - -#define REG_ALL (REG_AF | REG_BC | REG_DE | REG_HL) - struct node { enum node_type type; int value; diff --git a/others/sqrt.asm b/others/sqrt.asm new file mode 100644 index 0000000..4955171 --- /dev/null +++ b/others/sqrt.asm @@ -0,0 +1,163 @@ + ; + ; Z80 integer 32-bit square root + ; + ; by Oscar Toledo G. + ; + ; Creation date: Apr/07/2026. + ; + ; Based on code from https://stackoverflow.com/questions/15557667/square-root-by-bit-shift + ; + ; Input: + ; dehl = number + ; + ; Output: + ; hl = root. + ; +isqrt: + ld a,d + or e + or h + or l + ret z + + exx + ld hl,0 + ld de,0 + exx + ld ix,.bit +.0: + ld a,d + sub (ix+3) + jr nz,.1 + ld a,e + sub (ix+2) + jr nz,.1 + ld a,h + sub (ix+1) + jr nz,.1 + ld a,l + sub (ix+0) +.1: jr z,.2 + jr c,.2 + inc ix + inc ix + inc ix + inc ix + jp .0 + +.2: ld a,ixl + cp .bit+16*4 + jr z,.3 + exx + push de + push hl + ld a,l + add a,(ix+0) + ld l,a + ld a,h + adc a,(ix+1) + ld h,a + ld a,e + adc a,(ix+2) + ld e,a + ld a,d + adc a,(ix+3) + ld d,a + exx + ld a,l + exx + sub l + exx + ld a,h + exx + sbc a,h + exx + ld a,e + exx + sbc a,e + exx + ld a,d + exx + sbc a,d + exx + jp c,.5 + ld a,l + exx + sub l + exx + ld l,a + ld a,h + exx + sbc a,h + exx + ld h,a + ld a,e + exx + sbc a,e + exx + ld e,a + ld a,d + exx + sbc a,d + pop hl + pop de + exx + ld d,a + + exx + srl d + rr e + rr h + rr l + ld a,l + add a,(ix+0) + ld l,a + ld a,h + adc a,(ix+1) + ld h,a + ld a,e + adc a,(ix+2) + ld e,a + ld a,d + adc a,(ix+3) + ld d,a + exx + jp .4 + +.5: + exx + pop hl + pop de + srl d + rr e + rr h + rr l + exx +.4: + inc ix + inc ix + inc ix + inc ix + jp .2 +.3: + exx + ret + +.bit: + dw $0000,$4000 + dw $0000,$1000 + dw $0000,$0400 + dw $0000,$0100 + dw $0000,$0040 + dw $0000,$0010 + dw $0000,$0004 + dw $0000,$0001 + dw $4000,$0000 + dw $1000,$0000 + dw $0400,$0000 + dw $0100,$0000 + dw $0040,$0000 + dw $0010,$0000 + dw $0004,$0000 + dw $0001,$0000 + diff --git a/others/test_sqrt.bas b/others/test_sqrt.bas new file mode 100644 index 0000000..e115f21 --- /dev/null +++ b/others/test_sqrt.bas @@ -0,0 +1,20 @@ + ' + ' Test Z80 integer square root + ' + ' by Oscar Toledo G. + ' + ' Creation date: Apr/07/2026. + ' + + PRINT AT 33, USR sqrt(16384) + PRINT AT 65, USR sqrt(10000) + PRINT AT 97, USR sqrt(100) + + WHILE 1: WEND + +ASM SQRT: +ASM LD DE,0 +ASM CALL isqrt +ASM RET + + ASM INCLUDE "sqrt.asm" diff --git a/utility/notes.c b/utility/notes.c index 74dd68a..5545b45 100644 --- a/utility/notes.c +++ b/utility/notes.c @@ -50,38 +50,69 @@ int main(int argc, char *argv[]) int d; int e; int base_freq; + double divisor; + int offset; int max; int value; if (argc == 1) { fprintf(stderr, "\n"); fprintf(stderr, "Usage:\n"); - fprintf(stderr, " notes [-sn] 3579545 >source.asm\n"); + fprintf(stderr, " notes 3579545 32 0 >source.asm\n"); + fprintf(stderr, " notes -sn 3579545 32 0 >source.asm\n"); + fprintf(stderr, " notes -n 1789773 16 -1 >source.asm\n"); fprintf(stderr, "\n"); fprintf(stderr, "It will generate an assembler note table for the given\n"); fprintf(stderr, "frequency. This is useful in my CVBasic compiler\n"); fprintf(stderr, "because I need 3 tables: 3.58 mhz, 4.00 mhz, and 2.00 mhz.\n"); fprintf(stderr, "Of course, doing these tables manually is a chore.\n"); fprintf(stderr, "\n"); + fprintf(stderr, "The second number is the frequency divisor (32 for\n"); + fprintf(stderr, "AY-3-8910, SN76489, and the triangle in NES/Famicom, or\n"); + fprintf(stderr, "16 for the rectangle channels in NES/Famicom)"); + fprintf(stderr, "\n"); + fprintf(stderr, "The third number is the offset to the final value\n"); + fprintf(stderr, "(0 for AY-3-8910 and SN76489, or -1 for NES/Famicom)\n"); + fprintf(stderr, "\n"); fprintf(stderr, "The values are valid for AY-3-8910 or SN76489 chips.\n"); fprintf(stderr, "\n"); - fprintf(stderr, "It will warn you if the final value exceeds 4095 or\n"); - fprintf(stderr, "1023 (if the -sn option is used).\n"); + fprintf(stderr, "It will warn you if the final value exceeds 4095, or\n"); + fprintf(stderr, "1023 (-sn option), or 2047 (-n option)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Base musical frequencies are output in stderr, and\n"); fprintf(stderr, "assembler code in stdout.\n"); fprintf(stderr, "\n"); exit(1); } - fprintf(stderr, "Notes v0.1.0 Aug/07/2024 by Oscar Toledo G. https://nanochess.org\n"); + fprintf(stderr, "Notes v0.2.0 Aug/24/2025 by Oscar Toledo G. https://nanochess.org\n"); - if (argv[1][0] == '-' && tolower(argv[1][1] == 's') && tolower(argv[1][2]) == 'n') { - base_freq = atoi(argv[2]); + c = 1; + if (argv[c][0] == '-' && tolower(argv[c][1]) == 's' && tolower(argv[c][2]) == 'n') { + c++; max = 1023; + } else if (argv[c][0] == '-' && tolower(argv[c][1]) == 'n') { + c++; + max = 2047; } else { - base_freq = atoi(argv[1]); max = 4095; } + if (c >= argc) { + fprintf(stderr, "Missing base frequency.\n"); + exit(1); + } + base_freq = atoi(argv[c]); + c++; + if (c >= argc) { + fprintf(stderr, "Missing divisor.\n"); + exit(1); + } + divisor = atoi(argv[c]); + c++; + if (c >= argc) { + fprintf(stderr, "Missing offset.\n"); + exit(1); + } + offset = atoi(argv[c]); fprintf(stdout, "\t;\n"); fprintf(stdout, "\t; Musical notes table.\n"); fprintf(stdout, "\t;\n"); @@ -91,7 +122,8 @@ int main(int argc, char *argv[]) for (c = 2; c < 8; c++) { switch (c) { case 2: - fprintf(stdout, "\t; Values for %4.2f mhz.\n", base_freq / 1e6); + fprintf(stdout, "\t; Values for %4.2f mhz. / %d, offset %d\n", + base_freq / 1e6, (int) divisor, (int) offset); fprintf(stdout, "\t; 2nd octave - Index 1\n"); break; case 3: @@ -118,7 +150,7 @@ int main(int argc, char *argv[]) fprintf(stdout, "\tdw "); for (d = 0; d < e; d++) { fprintf(stderr, "%4.1f%s", frequencies[d], (d == e - 1) ? "\n" : ","); - value = (int) (base_freq / 32.0 / frequencies[d] + 0.5); + value = (int) (base_freq / divisor / frequencies[d] + 0.5) + offset; if (value > max) fprintf(stderr, "Warning: Exceeded range for octave %d, note %d\n", c, d); fprintf(stdout, "%d%c", value, (d == e - 1) ? '\n' : ',');