WDC816CC calling convention

Building your first 6502-based project? We'll help you get started here.
Post Reply
altera
Posts: 5
Joined: 23 Oct 2016

WDC816CC calling convention

Post by altera »

Hey everyone, I'm in the middle of building my first 128kByte 65816 based machine with SPI, Serial and AVR based ram loader and clock control. Address decoding is done using a Altera CPLD (MAX7000), this gives me quite a bit of flexibility.

I'm learning the ropes by building it on a breadboard first and plan to make a real PCB in the later stages. The AVR allows me to control the clock pulse by pulse inspecting the address and data lines as well as frequency control from 1kHz to 10MHz. Currently it is executing a very simply assembler program that creates output using a serially connected VT100 terminal (currently Putty). Total code size is 354 bytes :) I'm pleased that it runs at 10MHz on the breadboard.

Now to the question, is there any documentation on the WDC816CC calling convention? I'd like to mix C and ASM in my home brewed OS to go along with the hardware. The PDF from WDC is woefully short on information.
Attachments
board with labels
board with labels
first output of machine.
first output of machine.
hex file uploader and clock controller.
hex file uploader and clock controller.
altera
Posts: 5
Joined: 23 Oct 2016

Re: WDC816CC calling convention

Post by altera »

Of course reading the doc further I find my answer on page 21. Will answer my own question in more detail as i experiment with it.
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: WDC816CC calling convention

Post by BigEd »

Welcome! It's a good question you've posed. Looking forward to hearing more.
User avatar
Dr Jefyll
Posts: 3525
Joined: 11 Dec 2009
Location: Ontario, Canada
Contact:

Re: WDC816CC calling convention

Post by Dr Jefyll »

Welcome, altera -- nice project! Not too shabby, for someone who's just learning the ropes! 8)

It looks like one of Daryl's SPI Controller Devices you've got there, am I right?

cheers,
Jeff
In 1988 my 65C02 got six new registers and 44 new full-speed instructions!
https://laughtonelectronics.com/Arcana/ ... mmary.html
altera
Posts: 5
Joined: 23 Oct 2016

Re: WDC816CC calling convention

Post by altera »

Thanks, I'm very happy with how it's turning out.

That's right that is one of Daryl's SPI controllers. Works well!
altera
Posts: 5
Joined: 23 Oct 2016

Re: WDC816CC calling convention

Post by altera »

Alright found a few minutes after work here, don't have a whole lot of time during the week. I'm going to keep a running log of the investigation here until I collect it all. Maybe I can write it all up when done.

Compiling the following code with WDC816CC

Code: Select all

void test()
{
	const char * buf = "hello world!\r\n";	
	console_write(buf);
}
this code assumes that console_write is defined elsewhere. It will in fact be in ASM in a later post. Compiled using:

Code: Select all

wdc816cc.exe -a test.c
produces the following code, comments are mine. If I understood anything wrong feel free to correct me.

Code: Select all

;:ts=8
R0	equ	1
R1	equ	5
R2	equ	9
R3	equ	13
	code
	xdef	__test
	func
__test:
	
	longa	on		; clearly the compiler is assuming we are running A, X and Y as 16 bits.
	longi	on		; clearly the compiler is assuming we are running A, X and Y as 16 bits.
	
	
	tsc				; Transfer Stack Pointer to 16-bit Accumulator
	sec				; set carry flag -- WHY?
	sbc	#L2			; subtract L2 = 2 from A store result in A --  create two bytes of storage space on stack, remember stack GROWS DOWN!
	tcs				; Transfer 16-bit Accumulator to Stack Pointer
	phd				; Push Direct Page Register to stack		-- keep a copy of the old direct page.
	tcd				; Transfer 16-bit Accumulator to Direct Page Register -- point the direct page pointer to the stack FAST ACCESS to variables
	

	
buf_1	set	0
	lda	#<L1			; load address of string
	sta	<L3+buf_1	; store the address at L3 + buf_1.
	pei	<L3+buf_1	; Push Effective Indirect Addres, aka value in DirectPage 1 as an address pushed onto Stack - the address of the string... Couldn't this be done easier? PEA #L1
	
	; looks like the string is now on the stack.
	; for the function it will actually be the 2nd piece on the stack since
	; a call will push the return address L4 on the stack.
	
	jsr	__console_write
	
	; Functions called by a C function and C functions themselves return values in the X 
	; register and the Accumulator. The high word of the result, if any, is in the X 
	; register, while the low word is in the Accumulator.
		
L4:
	pld				; Pull Direct Page Register = e.g. restore the DP to what it was at the beginning of the Func.
	tsc				; Transfer Stack Pointer to 16-bit Accumulator
	clc				; Clear carry
	adc	#L2		; add 2
	tcs				; Transfer 16-bit Accumulator to Stack Pointer
	rts				; Done baby, no return values.
L2	equ	2
L3	equ	1
	ends
	efunc
	data
L1:
	db	$68,$65,$6C,$6C,$6F,$20,$77,$6F,$72,$6C,$64,$21,$0D,$0A,$00
	ends
	xref	__console_write
	end
next the ASM code __console_write and the ASM code that loads this guy.
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: WDC816CC calling convention

Post by BigDumbDinosaur »

Some comments...
  • altera wrote:

    Code: Select all

    ;:ts=8
    R0       equ 1
    R1       equ 5
    R2       equ 9
    R3       equ 13
             code
             xdef __test
             func
    __test:	
             longa on              ; clearly the compiler is assuming we are running A, X and Y as 16 bits.
             longi on              ; clearly the compiler is assuming we are running A, X and Y as 16 bits.
             tsc                   ; Transfer Stack Pointer to 16-bit Accumulator
             sec                   ; set carry flag -- WHY?
             sbc #L2               ; subtract L2 = 2 from A store result in A -- create two bytes of storage space on stack,
                                     remember stack GROWS DOWN!
             tcs                   ; Transfer 16-bit Accumulator to Stack Pointer
  • longa on is the WDC assembler's pseudo-op that says to generate 16 bit immediate mode operands for accumulator instructions, such as LDA #<operand>. Similarly, longi on tells the assembler to generate 16 bit immediate mode operands for index register instructions, e.g., LDX #<operand>. The compiler itself isn't assuming anything, as it was responsible for generating those pseudo-ops.
  • Implied, but not stated, is that the compiler generated assembly language source code corresponding to main() to set all registers to 16 bits, likely a REP #%00110000 instruction. Without that happening, the 16 bit operand assembled for the SBC #L2 instruction could be wrong interpreted by the MPU as an 8 bit load followed by a BRK instruction.
  • TSC and TCS are always 16 bit transfers, regardless of the state of the m bit in the status register (SR). The C in these mnemonics is the hint that this is the case.
  • Carry must be set before subtraction, as the SBC instruction means subtract with carry. In this case, carry is an inverted borrow. We don't know what the state of carry might be at the start of this routine, so carry must be explicitly set.
    Quote:

    Code: Select all

             phd                   ; Push Direct Page Register to stack -- keep a copy of the old direct page.
             tcd                   ; Transfer 16-bit Accumulator to Direct Page Register --
                                     point the direct page pointer to the stack FAST ACCESS to variables
    
    buf_1    set 0
             lda #<L1              ; load address of string
             sta <L3+buf_1         ; store the address at L3 + buf_1.
             pei <L3+buf_1         ; Push Effective Indirect Addres, aka value in DirectPage 1 as an address pushed onto Stack -
                                     the address of the string...Couldn't this be done easier? PEA #L1
  • Pointing DP at the stack is convenient for programming purposes and assists in generating fully relocatable code. However, unless DP starts on a physical RAM page, that is, on $00xx00, where xx is any page in the range $000000-$00FFFF, the performance gain normally seen with direct page accesses will be largely negated, as each load/store access will use the same number of clock cycles as an absolute load/store.

    It should be noted that there is a potential booby trap involved in pointing DP at the stack. If the local operating environment doesn't correctly preserve the MPU's state during API calls or within interrupt handlers, the stack could get trashed due to the operating environment blindly writing on the relocated direct page, inadvertently stepping on parameters, return addresses, etc.
  • The LDA #<L1 instruction could be ambiguous. Not seeing the entire program, I might be inclined to think that only the LSB of L1 would be loaded into the A-accumulator, with who-knows-what loaded into the B-accumulator. You might be reaching for the reset push button after the 65C816 executes that instruction. :roll:
  • PEA is an immediate mode instruction, which is why it isn't used here. If PEA were used the program would have to be self-modifying, which would be unsuitable if the code is to be burned into ROM. Obviously, the WDC compiler can't make any assumptions in that regard, which explains the presence of PEI.
  • The instructions

    Code: Select all

             sta <L3+buf_1
             pei <L3+buf_1
    are odd. The < operator implies least significant byte (LSB) operand generation. However, L3 is defined as 1, making it resolvable to eight bits. Hence there would be no need to use the < operator. Of course, since L3 is defined at the end of the source code, the above instructions constitute a forward reference (more on that below).

    Incidentally, I see nothing defining BUF_1.
    Quote:

    Code: Select all

                                   ; looks like the string is now on the stack.
                                   ; for the function it will actually be the 2nd piece on the stack since
                                   ; a call will push the return address L4 on the stack.
  • Actually, the string itself is not on the stack, only a pointer to it. The string could be almost anywhere in RAM and in fact, would normally be linked into the data section of the object file. Given the fact that the '816 has separate registers for the program and data banks (the PB and DB registers, respectively), the string could be loaded into a different bank than the executable code.
    Quote:

    Code: Select all

             jsr __console_write
             
                                   ; Functions called by a C function and C functions themselves return values in the X 
                                   ; register and the Accumulator. The high word of the result, if any, is in the X 
                                   ; register, while the low word is in the Accumulator.
    
    L4:      pld                   ; Pull Direct Page Register = e.g. restore the DP to what it was at the beginning of the Func.
             tsc                   ; Transfer Stack Pointer to 16-bit Accumulator
             clc                   ; Clear carry
             adc #L2               ; add 2
             tcs                   ; Transfer 16-bit Accumulator to Stack Pointer
             rts                   ; Done baby, no return values.
    
    L2       equ 2
    L3       equ 1
             ends
             efunc
             data
    L1:      db $68,$65,$6C,$6C,$6F,$20,$77,$6F,$72,$6C,$64,$21,$0D,$0A,$00
             ends
             xref __console_write
             end
  • The code at L4 is figuring that the accumulator is set to 16 bits upon return from __CONSOLE_WRITE. That is a potentially dangerous assumption to make, given that __CONSOLE_WRITE could be a ROM API that doesn't fully preserve the MPU state. :D
  • If possible, assembly-time constants such as L2 and L3 should be declared *before* any other reference is made to them. Otherwise, a forward reference condition occurs, which may trigger an assembly-time error or cause the assembler to needlessly treat these as 16 bit values and generate less efficient code. Direct page locations should always be declared before reference so the assembler understands that they are to be treated as eight bit addresses when an instruction references them.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
altera
Posts: 5
Joined: 23 Oct 2016

Re: WDC816CC calling convention

Post by altera »

Thanks for the insights! The ASM function is coming next but not likely to be today unfortunately.

Side note, all the functions are under my control of course so I can make them behave as required.
Post Reply