Jump to content
IGNORED

Wizzy on the 7800?


karri

Recommended Posts

7 minutes ago, RevEng said:

From the first posted script, try the values in the comments instead. Bracketed values show the link to the 7800 bit ordering...

 

def map160B(val):
    ''' 3276 1054 '''
    ret = 0
    if (val & 1) == 1:
        ret = ret | 4    # 64 (6)
    if (val & 2) == 2:
        ret = ret | 8    #128 (7)
    if (val & 4) == 4:
        ret = ret | 64   #  4 (2)
    if (val & 8) == 8:
        ret = ret | 128  #  8 (3)

    if (val & 16) == 16: 
        ret = ret | 1    # 16 (4)
    if (val & 32) == 32: 
        ret = ret | 2    # 32 (5)
    if (val & 64) == 64: 
        ret = ret | 16   #  1 (0)
    if (val & 128) == 128:
        ret = ret | 32   #  2 (1)
    return ret

 

Transparent color indexes are at 0,4,8,16, so you'd need to skip those in your mapping.

Thanks!

You are perfectly right. This works. I just don't understand why.
Actually I don't have any pixels with values 4, 8, 12 in my source images. All transparency is at index 0.

 

Edit! Now I get it! I was just thinking along the wrong lines. Thanks! Your explanation made it clear.

  • Like 1
Link to comment
Share on other sites

14 minutes ago, karri said:

Edit! Now I get it! I was just thinking along the wrong lines. Thanks! Your explanation made it clear.

You're welcome! I know from personal experience it's super easy to get turned around the wrong way between the png indexes and the 7800 indexes.

Link to comment
Share on other sites

I have been wondering what would be the best way to save progress on a game like Wizzy. As the carts do not have writable storage my choices may be

- a password system

- a joystick port "savekey"

- or just have a menu that allows you to choose which mission to run

 

There is a certain charm in having to earn your upgrades. So I am thinking about the savekey option. It would be possible to encode stuff into one "high score" value.

 

My original plan was to have 7 areas for Wizzy to explore. Every successful area has an artifact that grants Wizzy a new skill when used. So essentially I need at least 7 bits to keep track of which artifacts Wizzy has found.

  • Like 2
Link to comment
Share on other sites

I kind of like the TIA Tracker. But for Wizzy I would like to use a cart with a 3 channel square wave plus a noise channel.

Is there some modular driver that could be used with the 7800 to play Furnace tracker sounds. I merely need control over volume and pitch. The chip I am experimenting with is SN76489. A fairly common thing in retro consoles.

Perhaps I could modify the TIA Tracker driver for furnace data?

Link to comment
Share on other sites

My Wizzy is going strong on the new SN cart by @Eagle and @rj1307.

 

Here is a first screenshot.

IMG_20230109_153522.thumb.jpg.123f048d60a87325dac3ed3aa5b85a78.jpg

What makes this new cart amazing is the bank flipping. If Wizzy is going right I can make her go left by just setting a bit to flip the animation.

 

This is also very interesting when you look at the memory consumtion.

Name                   Start     End    Size  Align
----------------------------------------------------
EXEHDR                000000  00007F  000080  00001
ZEROPAGE              000040  000059  00001A  00001
EXTZP                 00005A  00005B  000002  00001
DATA                  001800  00187A  00007B  00001
BSS                   00187B  001CCF  000455  00001
CODE                  008000  009A9C  001A9D  00001
RODATA                009B00  009BC5  0000C6  00001
B008                  00A000  00AFFF  001000  00001 - Wizzy running
W000                  00A000  00AFFF  001000  00001 - Wizzy standing
M000                  00D000  00DFFF  001000  00001 - World map to $5000 RAM
T000                  00D000  00DFFF  001000  00001 - Tiles to $6000 RAM
T001                  00D000  00DFFF  001000  00001 - Tiles to $7000 RAM
R000                  00E000  00E13E  00013F  00001 - Some level garbage that should be in the resident segment.
STARTUP               00FF21  00FF5B  00003B  00001
ONCE                  00FF5C  00FF79  00001E  00001
ENCRYPTION            00FF7A  00FFF9  000080  00001
VECTORS               00FFFA  00FFFF  000006  00001

 

The Wizzy animations take up just one slot $A000.

The generic cart load buffer takes one slot $D000.

And I still have not started to code in the NPC's and enemies at  $C000 and $E000.

The sfx sound effects are already in the code and there is tons of free space still.

Thanks for making coding on the 7800 fun!

 

PS. I just had a thought. The $D000 needs to be used mainly between levels when the scenery changes. As it is mostly useless in other times it could be used for music! I mean at the end of the level you fade out whatever is playing and when the next level is up ane running you se up a new tune. The actual interruptor and player code would be in main memory but the notes could be flipped in from the banks.

 

PPS. I just had a glance at the vgc-player with compression support. It relies heavily on self-modifying code. So I believe it could live at the lowest RAM block at $4000. Now all the RAM is accounted for :) 

 

PPPS. In case some SN cart owner wants to build code for the cc65 you  can look at my config file for the linker of how to direct code segments for the SN cart.

 

# Atari VCS 7800 linker configuration file for cc65

SYMBOLS {
    __STACKSIZE__:        type = weak, value = $0600;
    __CARTSIZE__:         type = weak, value =  $c000;
    __EXEHDR__:           type = import;
    __VEC_BOTTOM__:       value = $fffa, type = export;
    __VEC_SIZE__:         value = $6, type = export;
    __ENCRYPT_BOTTOM__:   value = $ff7a, type = export;
    __ENCRYPT_SIZE__:     value = $80, type = export;
    __INIT_TOP__:         value = __ENCRYPT_BOTTOM__, type = export;
    __INIT_SIZE__:        value = 89, type = export;
    __INIT_BOTTOM__:      value = __INIT_TOP__ - __INIT_SIZE__, type = export;
    __BANK7_BOTTOM__:     value = $f000, type = export;
    __BANK7_SIZE__:       value = __INIT_BOTTOM__ - __BANK7_BOTTOM__, type = export;
    __BANK6_BOTTOM__:     value = $e000, type = export;
    __BANK6_SIZE__:       value = $1000, type = export;
    __BANK5_BOTTOM__:     value = $d000, type = export;
    __BANK5_SIZE__:       value = $1000, type = export;
    __BANK4_BOTTOM__:     value = $c000, type = export;
    __BANK4_SIZE__:       value = $1000, type = export;
    __BANK3_BOTTOM__:     value = $b000, type = export;
    __BANK3_SIZE__:       value = $1000, type = export;
    __BANK2_BOTTOM__:     value = $a000, type = export;
    __BANK2_SIZE__:       value = $1000, type = export;
    __BANK01_BOTTOM__:    value = $8000, type = export;
    __BANK01_SIZE__:      value = $2000, type = export;

    __BANK_SIZE__:        value = $1000, type = export;
    __WBANK_BOTTOM__:     value = $a000, type = export;
    __TBANK_BOTTOM__:     value = $d000, type = export;
    __TBANK_TOP__:        value = $d000, type = export;
}

MEMORY {
    ZP:     file = "", define = yes, start = $0040, size = $00C0, type = rw;
    SP:     file = "", define = yes, start = $0140, size = $00C0, type = rw;
    RAM1:   file = "", define = yes, start = $1800, size = $0840, type = rw;
    RAM2:   file = "", define = yes, start = $2100, size = $0040, type = rw;
    RAM3:   file = "", define = yes, start = $2200, size = $0600, type = rw;
    # For emulators you also need a header file
    HEADER: file = %O,               start = $0000, size = 128;
    # Wizzy cartridge rom.
    # $4000..$7fff Banked RAM
    # - start must be a multiple of $1000
    # - ROM must end at $ff79
    RESIDENT:   file = %O, define = yes, start = __BANK01_BOTTOM__, size = __BANK01_SIZE__, type = ro, fill = yes, fillval = $ff;
    WIZZY000:   file = %O, define = yes, start = __BANK2_BOTTOM__, size = __BANK2_SIZE__, type = ro, fill = yes, fillval = $ff;
    MLEVEL000:  file = %O, define = yes, start = __BANK3_BOTTOM__, size = __BANK3_SIZE__, type = ro, fill = yes, fillval = $ff;
    LEVEL000:   file = %O, define = yes, start = __BANK4_BOTTOM__, size = __BANK4_SIZE__+__BANK5_SIZE__, type = ro, fill = yes, fillval = $ff;
    RLEVEL000:  file = %O, define = yes, start = __BANK6_BOTTOM__, size = __BANK6_SIZE__, type = ro, fill = yes, fillval = $ff;
    # Extra area. Not used by MARIA.
    ROMH:   file = %O, define = yes, start = __BANK7_BOTTOM__, size = __BANK7_SIZE__, type = ro, fill = yes, fillval = $ff;
    # Startup code. Good to be close to ROMS.
    ROMS:   file = %O, define = yes, start = __INIT_BOTTOM__, size = __INIT_SIZE__, type = ro, fill = yes, fillval = $ff;
    # Encryption stuff
    ROME:   file = %O, start = __ENCRYPT_BOTTOM__, size = __ENCRYPT_SIZE__, type = ro, fill = yes, fillval = $ff;
    # Interrupt vectors
    ROMV:   file = %O, start = __VEC_BOTTOM__, size = __VEC_SIZE__, type = ro, fill = yes, fillval = $ff;

    # Bank 8
    WIZZY008:    file = "wizzybanks.dat", define = yes, start = __WBANK_BOTTOM__, size = __BANK_SIZE__, type = ro, fill = yes, fillval = $ff;
    # Bank 9
    TILE000:    file = "wizzybanks.dat", define = yes, start = __TBANK_BOTTOM__, size = __BANK_SIZE__, type = ro, fill = yes, fillval = $ff;
    # Bank 10
    TILE001:    file = "wizzybanks.dat", define = yes, start = __TBANK_TOP__, size = __BANK_SIZE__, type = ro, fill = yes, fillval = $ff;
}

SEGMENTS {
    ZEROPAGE:   load = ZP,               type = zp;
    EXTZP:      load = ZP,               type = zp;
    EXEHDR:     load = HEADER,           type = ro,  define = yes;
    CODE:       load = RESIDENT,         type = ro,  define = yes;
    RODATA:     load = RESIDENT,         type = ro,  define = yes, align = 256;
    DATA:       load = RESIDENT, run = RAM1, type = rw,  define = yes;
    W000:       load = WIZZY008,         type = ro,  define = yes;
    M000:       load = MLEVEL000,        type = ro,  define = yes;
    L000:       load = LEVEL000,         type = ro,  define = yes;
    R000:       load = RLEVEL000,        type = ro,  define = yes;
    BSS:        load = RAM1,             type = bss, define = yes;
    STARTUP:    load = ROMS,             type = ro,  define = yes;
    ONCE:       load = ROMS,             type = ro,  define = yes;
    ENCRYPTION: load = ROME,             type = ro   define = yes;
    VECTORS:    load = ROMV,             type = ro,  define = yes;
    B008:       load = WIZZY000,         type = ro,  define = yes;
    T000:       load = TILE000,          type = ro,  define = yes;
    T001:       load = TILE001,          type = ro,  define = yes;
}

FEATURES {
    CONDES: type    = constructor,
            label   = __CONSTRUCTOR_TABLE__,
            count   = __CONSTRUCTOR_COUNT__,
            segment = ONCE;
    CONDES: type    = destructor,
            label   = __DESTRUCTOR_TABLE__,
            count   = __DESTRUCTOR_COUNT__,
            segment = RODATA;
    CONDES: type    = interruptor,
            label   = __INTERRUPTOR_TABLE__,
            count   = __INTERRUPTOR_COUNT__,
            segment = RODATA,
            import  = __CALLIRQ__;
}

 

  • Like 4
Link to comment
Share on other sites

After a lot of fiddling around with the code I managed to pack my first Furnace tracker file to the compressed runlength format. The size is just 1700 bytes which will well fit into my 4096 byte buffer. The music was from the demo folder in furnace. Enjoy

 

 

 

The resulting file size is just 8% of the original vgm file. And I just export the SN76489AN chip music to the vgm file.

 

  • Like 5
Link to comment
Share on other sites

As a newbie on the 7800 I don't know how much people read manuals of a game. The problem I see with Wizzy is that it is a bit difficult to create the right mood for an adventure game. Part of the mood comes from the music and the graphics.

 

So I have been thinking on writing a small "novel" that doubles as a cheat sheet if you get stuck. Putting all this text in the game would not work. There will just be short sentences said by the npc's that hopefully nudge you in the right direction.

 

Here is my first land "Suvi" as a taster. Suvi is the island where Wizzy has grown up and where our adventure begins.

Wizzy.pdf

If you plan to read my pdf above and want to give me positive feedback, please do so.

 

The plan is to have all 7 lands in the book. These adventures can be solved in any order. But the game in one land follows the storyline in the book. So this is kind of a cinematic adventure. But you cannot have different endings.

 

My plan is to create a single run of 40 copies containing the Wizzy game on a SN cart and a hand-bound small booklet around 90 pages. About same size as the cart. In order to keep them together.

  • Like 4
Link to comment
Share on other sites

I hate self modifying code... You can only run the code in RAM. It is difficult to read.

 

So I managed to initialize the vgc player and set the irq's at every frame. But playing the stuff turns the screen to purple after a while and the code breaks.

 

My first concern is the instruction set. Do I use instructions that don't work on the 7800?

 

Another thing that passed my mind is the rate 50Hz. I could block every 6th interrupt on NTSF 7800 to produce 50Hz for the music on both consoles.

 

Fortunately most of the self modifying code is in the huffman part and I don't really need it just now.

 

Edit: now I have fixed all crashes. The sound the vgcplayer produces is just noise right now. I assume the problem is in my misunderstanding of the original assembly syntax.

 

Well, I realize now that the SN76489AN looks very different in the vlcplayer sources.

 

How is the chip wired on the SN cart? Does it has just one data port? The original code referenced addresses $fe40, $fe43 and $fe4f. So I thought that they would be addresses on the SN76489AN.

 

; Write data to SN76489 sound chip
; A contains data to be written to sound chip
; clobbers X, A is non-zero on exit
sn_write:
    ldx #255
    stx SN76489AN_BASE+3
    sta SN76489AN_BASE+15
    inx
    stx SN76489AN_BASE
    lda SN76489AN_BASE
    ora #8
    sta SN76489AN_BASE
    rts ; 21 bytes

; Reset SN76489 sound chip to a default (silent) state
sn_reset:
    ; Zero volume on all channels
    lda #$9f
    jsr sn_write
    lda #$bf
    jsr sn_write
    lda #$df
    jsr sn_write
    lda #$ff
    jmp sn_write
Link to comment
Share on other sites

I had a song that played fine on the CPU, but when I played it on the DLI it started playing badly.

I still have no clue why :) No problem with other songs at all.

Worth remembering:

Always clear zero page at the beginning 

If you use NMI and jump by JMP (DLIVECTOR), set DLIVECTOR before you set DLL (otherwise you will get JMP ($0000))

Try to not use WSYNC

 

Link to comment
Share on other sites

1 hour ago, karri said:

How is the chip wired on the SN cart? Does it has just one data port? The original code referenced addresses $fe40, $fe43 and $fe4f. So I thought that they would be addresses on the SN76489AN.

It's just one $43F

 

EDIT:

Memory map BBC micro

 

$FE40	6522 System VIA	Output B / Input B	Sound & Keyboard
$FE41	6522 System VIA	Output A / Input A	
$FE42	6522 System VIA	Data Direction B	
$FE43	6522 System VIA	Data Direction A	
$FE44	6522 System VIA	T1 Low order Latches/Counter	
$FE45	6522 System VIA	T1 High order Counter	
$FE46	6522 System VIA	T1 Low order Latches	
$FE47	6522 System VIA	T1 High order Latches	
$FE48	6522 System VIA	T2 Low order Latches/Counter	
$FE49	6522 System VIA	T2 High order Counter	
$FE4A	6522 System VIA	Shift Register	
$FE4B	6522 System VIA	Auxiliary Control Register	
$FE4C	6522 System VIA	Peripheral Control Register	
$FE4D	6522 System VIA	Interrupt Flags %v12ALSBK	V=systemVia 1=Timer1 (100hz) 2=Timer2 (speech) A=Analog conv L=Lightpen S=Shift reg B=Vblank K=Keypress
$FE4E	6522 System VIA	Interrupt Enable	
$FE4F	6522 System VIA	Output A / Input A - No Handshake	

 

Edited by Eagle
Memory Map
  • Thanks 1
Link to comment
Share on other sites

5 minutes ago, Eagle said:

I had a song that played fine on the CPU, but when I played it on the DLI it started playing badly.

I still have no clue why :) No problem with other songs at all.

Worth remembering:

Always clear zero page at the beginning 

If you use NMI and jump by JMP (DLIVECTOR), set DLIVECTOR before you set DLL (otherwise you will get JMP ($0000))

Try to not use WSYNC

 

Thanks for the help. All my interrupts are the NMI's generated at the start and end zones. In cc65 I use interruptors so the system will make the calls to the interruptors automatically. I already have the sfx library in place with the TIA sound effects. It also uses interruptors. The only problem I had was to delay the interrupts until I had time to copy the vgcplayer to RAM and read in the bank with the correct music to $D000.
Without Huffman the size was about 1980 bytes. With Huffman 1700 bytes.

Link to comment
Share on other sites

@karri I used Huffmunch for YM and SN before.

Much easier and you don't need put your code in to Ram

It's not that good as Exomizer but has some advantages.

 

huffmunch_init
		lda #<vgm_data
		sta hm_node
		lda #>vgm_data
		sta hm_node+1
		ldx #00
		ldy #00
		jsr huffmunch_load
		rts

then

		jsr huffmunch_read

an you will get one byte

 

https://github.com/bbbradsmith/huffmunch

 

huffmunch.asm

  • Thanks 1
Link to comment
Share on other sites

20 minutes ago, Eagle said:

It's not that good as Exomizer but has some advantages.

Exomizer was used for packing the binary stuff. In the vgc player they used runlengths, lz and huffman. It is slower but much better compressed. So I can always fit my music into one 4k block. It is an important thing as the game is pretty large.

Link to comment
Share on other sites

This is how I'm changing bank $D000 to read data to play VGM

Also you can store/restore bankcounter when you access $dxxx for any other reasons (reading level data, map, etc.)

e.g.

		lda (snmusic),y
		sta SNBASE
		incbank
		....
		....

 

	.macro incbank 
	inc snmusic
	bne @+
	inc snmusic+1
	lda snmusic+1
	cmp #$E0
	bne @+
	lda #$D0
	sta snmusic+1
	inc bankcounter
	lda bankcounter
	sta $D000
@	.endm 

 

  • Thanks 1
Link to comment
Share on other sites

9 hours ago, Eagle said:

@karri can you send me your vgm example? I can't find this music in Furnace demos. (VGM, bin and exo files please)

Yes. I picked a tune in demos/sms/doorintosummer.fur, removed the Yamaha chip from the song and exported the SN chip as a vgm file. I can try to convert it to bin and exo also. I only needed the vgc packed version in Wizzy.

furnacetest.vgm

Link to comment
Share on other sites

Thanks. I don't know what I need yet. I may try both approaches and choose later.

 

Currently I am a bit stuck with a weird problem. If I copy my code from $d000 to $4000 if overwrites segment $5000 as well. And this happens before I execute the code at $4000. I wonder if my memcpy is bad in cc65 for the 7800?

 

I can easily fix this by copying stuff in order $4000, $5000, $6000, $7000. But I would like to know why any other order causes problems. Debugging is a bit difficult when running on real hw...

 

Edit: Case solved. When you use the linker to link in your code. Don't link it to address $D000 if you copy it to address $4000 for execution. When I debugged my map was using whatever happened to be in the $D000 bank. Sigh...

  • Like 1
Link to comment
Share on other sites

23 minutes ago, Eagle said:

Or just put $66 (vgm end) on the end of BIN file before you compress the file, then when you reach the end just cal huffmunch_load again. 

Thanks!

 

My setup that works is like:

RAM0       RAM1

TILES0     HUFFMUNCH player
TILES1     HFF music
FONT       HFF music
MAP        MAP

 

To my big surprise it was easy and straight forward to make this work in Wizzy.

 

The reason I put in the MAP in both banks was that scrolling is time consuming and I need the MAP all the time.

 

The nice thing is that the music just keeps going in the background and I don't have to think about it. The sfx sound effects seem to work nicely with the music. A big plus!

 

This also leaves me free to use bank $d000 at any time without concern of interrupts.

  • Like 1
Link to comment
Share on other sites

I have been playing with huffmunch a bit. It appears that single instruments doing vibrato or glides in frequency produce quite big files. Unfortunately I am very fond of flute-like sounds with glides and vibrato. But I still cannot get my vgcplayer to work properly.

 

If I start with a vgm file I understand that there is two ways to go:

 

1) Huffmunch way

- dump.py vgmconverter.py to convert vgm to raw

- huffmunch to convert raw to hff

+ simple player, easy to get to work, sounds good

 

2) Vgc way

- vgmpacker.py to convert vgm to vgc

+ Uses run-length encoding for every channel separately which compresses anything that is stable a LOT. Has also built-in huffman compression. But I have not been able to get it to work yet. The code is self-modifying so you need to run it in RAM.

 

I may do something wrong here. But I recently tried to build my small furnacetest.vgm with both tools.

1) Huffmunch way: furnacetest.hff 8416 5822 bytes
2) Vgc way: furnacetest.vgc 1949 bytes

 

It appears that I can get 4 times longer tunes if I can get the vgcplayer to work. (And if the code is fast enough to run during the blank period.)

 

Edit: I can only play previously processed huffmunch files like CollisionChaos.hff. When I compress my own stuff it does not work.

I wonder what the correct process is to convert a vgm file to hff format.

python2 vgmconvert.py file.vgm -q50 -o file.bin
huffmunch -B file.bin file.hff

 

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...