Building a new AY player.

This forum is about audio tools, including samplers, sound trackers, sound chip editors, how to do sound effects, etc...
JamesD
Flight Lieutenant
Posts: 358
Joined: Tue Nov 07, 2006 7:38 am

Post by JamesD »

Changed the non-Oric AY output by swapping

Code: Select all

STA AYBase
and

Code: Select all

STA AYBase+1
User avatar
Twilighte
Game master
Posts: 819
Joined: Sat Jan 07, 2006 12:07 am
Location: Luton, UK
Contact:

Post by Twilighte »

JamesD, you wrote..

Code: Select all

commands 
   sty   tempy 

   ; adjust the command pointer to get command table offset 
   and   #%01111111   ; mask off top bit 
   rol         ; bit shift = multiply by 2 (addresses are 2 bytes in size) 

   ; get command address from command pointer table (self modifying code) 
   tay 
   lda   (commandtable),y 
   sta   ijmp+1 
   lda   (commandtable),y 
   sta   ijmp+2 
ijmp   jmp   commandtable   ; self modifying code, playsong end replaced with current command address 


I think you meant to put an INY between the two lda(commandtable),y?
JamesD
Flight Lieutenant
Posts: 358
Joined: Tue Nov 07, 2006 7:38 am

Post by JamesD »

Twilighte wrote:JamesD, you wrote..

Code: Select all

commands 
   sty   tempy 

   ; adjust the command pointer to get command table offset 
   and   #%01111111   ; mask off top bit 
   rol         ; bit shift = multiply by 2 (addresses are 2 bytes in size) 

   ; get command address from command pointer table (self modifying code) 
   tay 
   lda   (commandtable),y 
   sta   ijmp+1 
   lda   (commandtable),y 
   sta   ijmp+2 
ijmp   jmp   commandtable   ; self modifying code, playsong end replaced with current command address 


I think you meant to put an INY between the two lda(commandtable),y?
Yup.

I deleted my posts from earlier today because I forgot I was dealing with a page 0 pointer. I'm definitely not used to the 6502.
JamesD
Flight Lieutenant
Posts: 358
Joined: Tue Nov 07, 2006 7:38 am

Post by JamesD »

I have a working version of the code that will call commands. To keep from having to test for zero and negative I changed the song end to 128 and just tested for negative.

Code: Select all

docomnd
;	beq	playsongend
	sty	temp	; save Y

	; self modifying code
	; register A contains the command number + 128
	; adjust the command pointer to get command table offset
;	and	#%01111111	; mask off top bit.  Not required due to ROL
	clc				; CLC for the ROL
					; ROL moves top bit to carry and previous carry goes to bottom bit
	rol				; bit shift = multiply by 2 (addresses are 2 bytes in size)

	; get command address from command pointer table (self modifying code)
	tay				; move the command table offset to Y
	lda	(cmds),y	; load the 1st byte of the command pointer into A
	sta	ijmp+1		; modify the 1st byte of the address in the jmp
	iny				; adjust offset for next byte
	lda	(cmds),y	; load the 2nd byte of the command pointer into A
	sta	ijmp+2		; modify the 2nd byte of the address in the jmp
	
	ldy	temp		; restore Y
ijmp	jmp	playsongend	; playsong end replaced with current command address

commandtable
	.word	playsongend		; 00 end of song
; additional command pointers go here
<edit>
btw, if all commands lie on one page you only need to modify one byte of the JMP and you can leave out 3 instructions.
<edit>
Actually, you can replace the CLC ROL with the AND and then use a table of 8 bit low address bytes which cuts the command table size in half.
JamesD
Flight Lieutenant
Posts: 358
Joined: Tue Nov 07, 2006 7:38 am

Post by JamesD »

I started converting this to a fully interrupt driven player. I had already converted all the other versions and decided I'd been slacking off too much on the Oric version. I'm also looking at implementing an Apple II version with Mockingboard support since it's similar to how the Oric works anyway.

Technically, the player interrupt is almost done. I need to add a flag to indicate we are playing a song and to finish the song end code.
Besides that it needs setup, remove, play song, stop song, etc... support routines so it can be used as a library. Oh yeah, and I haven't even begun to fix comments.

I also included most of the code to handle commands in this snippet.
The player should be small enough it could be placed in a BASIC string using VARPTR and routines could be called with USR functions... if Oric supports that anyway.

Code: Select all

;**************************************************************
;* our interrupt handler
;**************************************************************
_VBLIrq
	; save registers
	pha
	
	bit	VIA_T1C
	;	wait for interrupt, _WaitCount times
	lda	_WaitCount
	dec			; decrement the counter
	sta	_WaitCount
	bne	exit_int

#ifdef	USE_C02
	phx			;65c02
	phy			;65c02
#else
	txa
	pha
	tya
	pha
#endif	

	ldy	#1			; offset from song pointer, Y must be used for this addressing mode
	lda	(song),y	; get number of registers to modify
;	bmi	docomnd		; negative numbers are commands
	beq	playsongend	; check for end of song
	tax				; put it in X
	iny				; next byte in song

_reglop
	lda	(song),y	; get the register number to modify
;6502 ORIC AY Control using the VIA 6522 makes this slower than memory mapped AY chip
#ifdef	ORICAY
	sta	VIAIORA		; set the AY data register number
	lda	#$FF		; $FF = VIAIORA holds a register number
	sta	VIA_PCR		; Set the VIA 6522 control line Register(PCR)
	lda	#$DD		; $DD = VIAIORA is inactive 
	sta	VIA_PCR		; Set the VIA 6522 control line Register(PCR)
#else
	sta	AYBase+1	; set the AY data register number
#endif
	iny				; point to next byte in song
	lda	(song),y	; get the Value for the AY register
#ifdef	ORICAY
	sta	VIAIORA		; load the data register value
	lda	#$FD		; $FD = VIAIORA holds data for a preset register 
	sta	VIA_PCR		; Set the VIA 6522 control line Register(PCR)
	lda	#$DD		; $DD = VIAIORA is inactive
	sta	VIA_PCR		; Set the VIA 6522 control line Register(PCR)
#else
	sta	AYBase		; load the data register value
#endif
	iny				; point to next byte in song
	dex				; decrement the register count
	bne	_reglop		; keep looping if not done setting registers

;	iny				; point to next byte in song
	lda	(song),y	; get the new wait counter
	sta	_WaitCount	; save it

; update the pointer (16 bit song pointer + 8 bit index)
; (songLSB + y, songMSB + carry)
;	clc				; clear the carry for out 16 bit addition
	tya				; put pointer offset in A
	adc	song		; add low byte (LSB)
	sta	song		; update low byte
	lda	#0			; clear A
	adc	song+1		; add carry to high byte (MSB)
	sta	song+1		; update the song pointer


	; restore registers we saved
#ifdef	USE_C02
	ply			; 65c02
	plx			; 65c02
#else
	pla
	tay
	pla
	tax
#endif
exit_int
	pla

	rti



;======================
docomnd
;	beq	playsongend
	sty	temp	; save Y

	; self modifying code
	; register A contains the command number + 128
	; adjust the command pointer to get command table offset
;	and	#%01111111	; mask off top bit.  Not required due to ROL
	clc				; CLC for the ROL
					; ROL moves top bit to carry and previous carry goes to bottom bit
	rol				; bit shift = multiply by 2 (addresses are 2 bytes in size)

	; get command address from command pointer table (self modifying code)
	tay				; move the command table offset to Y
	lda	(cmds),y	; load the 1st byte of the command pointer into A
	sta	ijmp+1		; modify the 1st byte of the address in the jmp
	iny				; adjust offset for next byte
	lda	(cmds),y	; load the 2nd byte of the command pointer into A
	sta	ijmp+2		; modify the 2nd byte of the address in the jmp
	
	ldy	temp		; restore Y
ijmp	jmp	playsongend	; playsong end replaced with current command address

;===============================	
commandtable
	.word	playsongend		; 0 end of song
	.word	playsongend		; other commands would go here
;
JamesD
Flight Lieutenant
Posts: 358
Joined: Tue Nov 07, 2006 7:38 am

Post by JamesD »

Oricutron's video capture feature is pretty neat.
http://www.youtube.com/watch?v=webLPdJuSq0
JamesD
Flight Lieutenant
Posts: 358
Joined: Tue Nov 07, 2006 7:38 am

Post by JamesD »

Ok, after having to deal with some personal stuff, I'm ready to make this into a C library and/or library that can be called from BASIC.

If someone could point me to docs on interfacing assembly to the OSDC and ORIC BASIC that would help a lot.
<edit>
I'm planning on adding support for the Apple II Mockingboard so I'll probably be interfacing with CC65 as well.
User avatar
Dbug
Site Admin
Posts: 4437
Joined: Fri Jan 06, 2006 10:00 pm
Location: Oslo, Norway
Contact:

Post by Dbug »

To interface Assembler with C using the OSDK is actually simple.

You can take a look in the OSDK samples:
- C:\osdk\sample\mixed\hello_world_mixed

Basically, you have to define two elements:
- The fact that the compiler and linker can see the functions
- How to pass parameters

The first one is easy, you declare for the compile as you would normally do, using "extern" for the variables, and normal pre-declaration for functions. Then on the assembler side, just make sure that the symbol starts by a underscore.

Example:

test.c

Code: Select all

extern unsigned char  MyAssemblerByte;
void MyAssemblerFunction();

void main()
{
   MyAssemblerByte=23:
   MyAssemblerFunction();
}
bla.s

Code: Select all

_MyAssemblerByte dc.b 0

_ MyAssemblerFunction
   rts
That's it.

To pass parameters, either you use some global variables, as I did in the previous example, or you use stack parameter passing.

There is a zero page pointer called "sp", and you use the y register to access the stack frame, so for example a print function would look like that:

Code: Select all

	ldy #0
	lda (sp),y
	sta ...
	iny
	lda (sp),y
	sta ...
Then from C you can call it normally with parameter passed between parenthesis.

For the BASIC, there's nothing defined really, but it would make sense to define a standard interface to call assembler modules and pass parameters. The delicate part is to resize the memory usage (HIMEM) to avoid the BASIC variables to trash the assembler routine :)
JamesD
Flight Lieutenant
Posts: 358
Joined: Tue Nov 07, 2006 7:38 am

Post by JamesD »

Not quite as easy as CC65's Fastcall but pretty much what I expected.
JamesD
Flight Lieutenant
Posts: 358
Joined: Tue Nov 07, 2006 7:38 am

Post by JamesD »

As for BASIC, if ORIC supports VARPTR and USR, we could POKE the entire player into a string. I think the player is still under 200 bytes so it *should* fit in a string. It's the fastest way to load a program once the code is poked in place and saved back out. At least that was one of the better ways on the Z80 and 6809. If code relocatability isn't an issue on the 6502 anyway.

If that won't work I guess adjust high mem and POKE it there.
JamesD
Flight Lieutenant
Posts: 358
Joined: Tue Nov 07, 2006 7:38 am

Re: Building a new AY player.

Post by JamesD »

Digging up this old thread...

I got busy at work and didn't really have time to do anything with this and then I just sort of let it die.

Recently a thread about hacking Donkey Kong for the Apple II and the poster suggested using the Mockingboard.
FYI, the Mockingboard is a sound board that adds 1 or more AY chips to the Apple II with 6522 VIA chips.
Sound kinda familiar?
Anywho, I mentioned this code and I have now ported it to the Mockingboard and made a few of the changes I mentioned in this thread.
I had to convert the code to the CA65 assembler that comes with CC65 (the 6502 C compiler) but it should work on the Oric with minor changes.

Changes:
I seem to remember fixing a couple bugs in the original code though I'm not sure what they were.
The code supports endless song repeat.
The timer code now uses a free running timer instead of the one shot timer in the original.
It's set up as a series of calls to initialize the player and to disable it.
I set up a table for songs so that it can play a song from the list just by passing the song # in register A to a routine.

I don't remember if I ever posted code related to the command idea but it was in the last version of the program for the Oric. The player can be assembled with that enabled and you can execute pieces of your own code from within the player based on commands embedded in the song data.

The thread about patching Donkey Kong is over on the Atariage forums.
http://www.atariage.com/forums/topic/20 ... nkey-kong/

This player might be usable to play background tunes in the recent Donkey Kong game ported from BASIC to C.
All the background music is being written for channel A and as long as sound effects don't use that in game, there shouldn't be a conflict.
You would need to add code to disable and enable interrupts around any sounds in the code though.
Post Reply