dromaius816 - a new 65816 emulator

Topics pertaining to the emulation or simulation of the 65xx microprocessors and their peripheral chips.
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: dromaius816 - a new 65816 emulator

Post by BigDumbDinosaur »

daniMolina wrote:
So there are rules, but they have exceptions, but they too have exceptions, but they...

Fortunately, the exceptions are very few in number and readily remembered if you regularly write 816 code.  These days, I rarely have to look up an instruction in the Eyes and Lichty manual to clarify behavior vis a vis the m and x bits.

From my perspective, the 816 is easier to work with in software than in hardware.  Other than delving into best-case uses of the stack and direct page (and pointing the latter at the former), it seems I’ve expended much more time on resolving hardware matters than I have in figuring out software algorithms.  Some have complained about the m and x bits and what a pain they are in writing 816 code.  That’s the nature of the beast and represents a compromise that was needed to achieve 65C02 software compatibility.  All microprocessors have their warts—m and x are the 816’s software warts (the muxed data bus is the hardware wart).

In retrospect, it would have been better if the WDM ($42) placeholder opcode had been used as a prefix to tell the MPU to perform a 16-bit operation, e.g., $42 $A9 to encode a 16-bit, accumulator immediate-mode load.  However, I suspect that approach would have added complexity, demanded more die real estate and resulted in a longer development time, with attendant higher production costs and a greater risk of chip errata.  Also, at the time, WDC needed to work quickly to satisfy Apple’s deadlines for the IIGS.  So we are “stuck” with flipping some bits to change register widths.

Quote:
It's time to get a real, 100% accurate emulator, so I can test every opcode, one step at a time.

If you want a 100 percent, accurate emulation, you need a real 65C816 on which to experiment.  :D
x86?  We ain't got no x86.  We don't NEED no stinking x86!
John West
Posts: 383
Joined: 03 Sep 2002

Re: dromaius816 - a new 65816 emulator

Post by John West »

daniMolina wrote:
It's time to get a real, 100% accurate emulator, so I can test every opcode, one step at a time.
I expect the SNES simulator scene will have one, and it will be a very good one. Games on successful long-lived platforms tend to exploit every obscure quirk of their hardware. Whether it can be easily extracted from the rest of the simulator is another question.
daniMolina
Posts: 214
Joined: 25 Jan 2019
Location: Madrid, Spain

Re: dromaius816 - a new 65816 emulator

Post by daniMolina »

BigDumbDinosaur wrote:
If you want a 100 percent, accurate emulation, you need a real 65C816 on which to experiment.  :D
Absolutely no tinkering with any real hardware (beyond my laptop) is allowed while the whole family is stuck in a 35 square meter (aprox 375 sq feet) apartment :lol: :lol: :lol:

At least, it is something temporary and at some point during 2025 we'll be able to move to a our new house, where I will have a dedicate room for myself! Yay! But sure enough, a proper WDC65c816 will be there. I have some rough idea to build something that will allow my to trace all IOs, while single stepping the CPU.
John West wrote:
I expect the SNES simulator scene will have one, and it will be a very good one. Games on successful long-lived platforms tend to exploit every obscure quirk of their hardware. Whether it can be easily extracted from the rest of the simulator is another question.
I am tinkering with Crossrunner. An Apple IIGS emulator and it seems quite good. It has a debug mode that lets you debug, execute instruction by instruction... the usual stuff. Also, the late Andrew Jacobs emu816 is in my toolbox now.
daniMolina
Posts: 214
Joined: 25 Jan 2019
Location: Madrid, Spain

Re: dromaius816 - a new 65816 emulator

Post by daniMolina »

It may not be much but.... I now have a second opcode implemented!

Ok. No, it is not much to be honest... but I've been doing more things meanwhile.

The new opcode is XCE and with it, the ability to switch between native and emulation modes. The status register properly reacts to this, swapping the Unused and Break flag in emulation mode, for M and X in native. In the CPU Panel, I have both "versions" of the status register, and on the bottom, the current one, depending on E value.

SP high byte becomes 0x01 when the swap happens. As I cannot yet change M or X, nothing new is added to the Acc, X or Y registers.

Also, the option to step instruction by instruction is in place. Before, I could only step the clock. The emulator puts a breakpoint on the rising edge of VPA and then it stops. Keep in mind, this is not 100% correct, as VDA (which is not yet implemented) should be added to the ecuation.

Execution trace (replicating Andrew Jacobs emu816 trace, to compare both) and a memory explorer with a dissasembler are implemented too. I am adding opcodes to the disassembler as I go. As I am only working on 1 byte instructions (implied addressing) I have not paid too much attention to the addresing modes. Meanwhile, I am still reverse engineering the original emulator, to understand how the 6502 modes were implemented, and the same time, investigating the 65816 new modes.

I am attaching an excerpt of the execution trace. Some bits are still missing though.

Code: Select all

00:EAEA EA          NOP {dd:hhll} E=1 P=.....I.. A=00[00] X=00[00] Y=00[00] DP=0000 SP=01[FD] { xx xx xx xx } DBR=00
00:EAEB FB          XCE {dd:hhll} E=1 P=.....I.. A=00[00] X=00[00] Y=00[00] DP=0000 SP=01[FD] { xx xx xx xx } DBR=00
00:EAEC EA          NOP {dd:hhll} E=0 P=..MX.I.C A=00[00] X=00[00] Y=00[00] DP=0000 SP=[01FD] { xx xx xx xx } DBR=00
00:EAED EA          NOP {dd:hhll} E=0 P=..MX.I.C A=00[00] X=00[00] Y=00[00] DP=0000 SP=[01FD] { xx xx xx xx } DBR=00
00:EAEE EA          NOP {dd:hhll} E=0 P=..MX.I.C A=00[00] X=00[00] Y=00[00] DP=0000 SP=[01FD] { xx xx xx xx } DBR=00
00:EAEF FB          XCE {dd:hhll} E=0 P=..MX.I.C A=00[00] X=00[00] Y=00[00] DP=0000 SP=[01FD] { xx xx xx xx } DBR=00
00:EAF0 EA          NOP {dd:hhll} E=1 P=.....I.. A=00[00] X=00[00] Y=00[00] DP=0000 SP=01[FD] { xx xx xx xx } DBR=00
00:EAF1 EA          NOP {dd:hhll} E=1 P=.....I.. A=00[00] X=00[00] Y=00[00] DP=0000 SP=01[FD] { xx xx xx xx } DBR=00
00:EAF2 EA          NOP {dd:hhll} E=1 P=.....I.. A=00[00] X=00[00] Y=00[00] DP=0000 SP=01[FD] { xx xx xx xx } DBR=00
00:EAF3 FB          XCE {dd:hhll} E=1 P=.....I.. A=00[00] X=00[00] Y=00[00] DP=0000 SP=01[FD] { xx xx xx xx } DBR=00
00:EAF4 EA          NOP {dd:hhll} E=0 P=..MX.I.C A=00[00] X=00[00] Y=00[00] DP=0000 SP=[01FD] { xx xx xx xx } DBR=00
00:EAF5 EA          NOP {dd:hhll} E=0 P=..MX.I.C A=00[00] X=00[00] Y=00[00] DP=0000 SP=[01FD] { xx xx xx xx } DBR=00
00:EAF6 EA          NOP {dd:hhll} E=0 P=..MX.I.C A=00[00] X=00[00] Y=00[00] DP=0000 SP=[01FD] { xx xx xx xx } DBR=00
00:EAF7 FB          XCE {dd:hhll} E=0 P=..MX.I.C A=00[00] X=00[00] Y=00[00] DP=0000 SP=[01FD] { xx xx xx xx } DBR=00
00:EAF8 EA          NOP {dd:hhll} E=1 P=.....I.. A=00[00] X=00[00] Y=00[00] DP=0000 SP=01[FD] { xx xx xx xx } DBR=00
00:EAF9 EA          NOP {dd:hhll} E=1 P=.....I.. A=00[00] X=00[00] Y=00[00] DP=0000 SP=01[FD] { xx xx xx xx } DBR=00
00:EAFA EA          NOP {dd:hhll} E=1 P=.....I.. A=00[00] X=00[00] Y=00[00] DP=0000 SP=01[FD] { xx xx xx xx } DBR=00
00:EAFB FB          XCE {dd:hhll} E=1 P=.....I.. A=00[00] X=00[00] Y=00[00] DP=0000 SP=01[FD] { xx xx xx xx } DBR=00
Cheers!
Attachments
disassembler.png
disassembler.png (4.21 KiB) Viewed 3800 times
ram explorer.png
ram explorer.png (5.38 KiB) Viewed 3800 times
native.png
native.png (10.25 KiB) Viewed 3800 times
emulation.png
emulation.png (10.26 KiB) Viewed 3800 times
User avatar
BigEd
Posts: 11463
Joined: 11 Dec 2008
Location: England
Contact:

Re: dromaius816 - a new 65816 emulator

Post by BigEd »

good progress!
daniMolina
Posts: 214
Joined: 25 Jan 2019
Location: Madrid, Spain

Re: dromaius816 - a new 65816 emulator

Post by daniMolina »

And now, a complete disassembler! The original project had a 6502 disassembler, which I have extended to work with all 65816 opcodes and addressing modes.

When using immediate addresing, to choose between an 8bit or 16bit operand, the current M and X flags are taken into account. Also, the disassembler offers the option to follow the PC, so it will stay aligned with the execution flow.

I am aware of more complex solutions to the operand size problem, but I hope this is accurate enough for my needs.

Also... and of this, I am not sure... If the opcode targets the Accumulator, then the operand size depends only on the M flag. If the opcode targets X or Y, then it only depends on the X flag. It makes sense to me, but still...

PS. Do not try to make sense of the code in the image, I am using a 64kb image of random bytes.
Attachments
dis.png
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: dromaius816 - a new 65816 emulator

Post by BigDumbDinosaur »

daniMolina wrote:
If the opcode targets the Accumulator, then the operand size depends only on the M flag. If the opcode targets X or Y, then it only depends on the X flag. It makes sense to me, but still...

That is correct if an immediate-mode instruction is being disassembled.

Quote:
PS. Do not try to make sense of the code in the image, I am using a 64kb image of random bytes.

The disassembly of PEA at $0006 and PEI at $0026 is incorrect.  Where are the operands?
x86?  We ain't got no x86.  We don't NEED no stinking x86!
daniMolina
Posts: 214
Joined: 25 Jan 2019
Location: Madrid, Spain

Re: dromaius816 - a new 65816 emulator

Post by daniMolina »

BigDumbDinosaur wrote:
daniMolina wrote:
If the opcode targets the Accumulator, then the operand size depends only on the M flag. If the opcode targets X or Y, then it only depends on the X flag. It makes sense to me, but still...
That is correct if an immediate-mode instruction is being disassembled.
Awesome, thanks for the feedback!
BigDumbDinosaur wrote:
Quote:
PS. Do not try to make sense of the code in the image, I am using a 64kb image of random bytes.
The disassembly of PEA at $0006 and PEI at $0026 is incorrect.  Where are the operands?
Lost in the WDC datasheet I'm afraid.

In Table 5-5 Operation, Operation Codes, and Status Register, both PEA (opcode F4) and PEI (opcode D4) are listed as using the Stack, s addressing mode. Then...
Quote:
3.5.22 Stack-s.
Stack (s) addressing refers to all instructions that push or pull data from the stack, such as Push, Pull, Jump to Subroutine, Return from Subroutine, Interrupts, and Return from Interrupt. The bank address is always 0. Interrupt Vectors are always fetched from Bank 0.
Unlike most other addresing modes in the datasheet, gives no indication on how many bytes the operand has. Next source of information was Table 3-1 Addressing Mode Summary which, indeed, says that stack addresing mode opcodes use only 1 byte (So no operand). Other opcodes with this addresing mode (Again, always blindly trusting the datasheet) which I'm familiar with, from my limited experience with the 6502... PHA, PLA, PHX, PLX.... all single byte, no operand, so I didn't even question the other opcodes which were unfamiliar to me.

To be be fair, Table 5-4 Opcode Matrix actually lists PEA and PEI as being 3 bytes and 2 bytes long respectively. I've noticed I've made the same exact mistake with PER.

It's an easy fix though. In the disassembler I have :

Code: Select all

static const char OPCODE_NAMES[256][4] = {
//	 00     01     02     03     04     05     06     07     08     09     0A     0B     0C     0D     0E     0F
	"BRK", "ORA", "COP", "ORA", "TSB", "ORA", "ASL", "ORA", "PHP", "ORA", "ASL", "PHD", "TSB", "ORA", "ASL", "ORA",
	"BPL", "ORA", "ORA", "ORA", "TRB", "ORA", "ASL", "ORA", "CLC", "ORA", "INC", "TCS", "TRB", "ORA", "ASL", "ORA",
	"JSR", "AND", "JSR", "AND", "BIT", "AND", "ROL", "AND", "PLP", "AND", "ROL", "PLD", "BIT", "AND", "ROL", "AND",
	"BMI", "AND", "AND", "AND", "BIT", "AND", "ROL", "AND", "SEC", "AND", "DEC", "TSC", "BIT", "AND", "ROL", "AND",
	"RTI", "EOR", "WDM", "EOR", "MVP", "EOR", "LSR", "EOR", "PHA", "EOR", "LSR", "PHK", "JMP", "EOR", "LSR", "EOR",
	"BVC", "EOR", "EOR", "EOR", "MVN", "EOR", "LSR", "EOR", "CLI", "EOR", "PHY", "TCD", "JMP", "EOR", "LSR", "EOR",
	"RTS", "ADC", "PER", "ADC", "STZ", "ADC", "ROR", "ADC", "PLA", "ADC", "ROR", "RTL", "JMP", "ADC", "ROR", "ADC",
	"BVS", "ADC", "ADC", "ADC", "STZ", "ADC", "ROR", "ADC", "SEI", "ADC", "PLY", "TDC", "JMP", "ADC", "ROR", "ADC",
	"BRA", "STA", "BRL", "STA", "STY", "STA", "STX", "STA", "DEY", "BIT", "TXA", "PHB", "STY", "STA", "STX", "STA",
	"BCC", "STA", "STA", "STA", "STY", "STA", "STX", "STA", "TYA", "STA", "TXS", "TXY", "STZ", "STA", "STZ", "STA",
	"LDY", "LDA", "LDX", "LDA", "LDY", "LDA", "LDX", "LDA", "TAY", "LDA", "TAX", "PLB", "LDY", "LDA", "LDX", "LDA",
	"BCS", "LDA", "LDA", "LDA", "LDY", "LDA", "LDX", "LDA", "CLV", "LDA", "TSX", "TYX", "LDY", "LDA", "LDX", "LDA",
	"CPY", "CMP", "REP", "CMP", "CPY", "CMP", "DEC", "CMP", "INY", "CMP", "DEX", "WAI", "CPY", "CMP", "DEC", "CMP",
	"BNE", "CMP", "CMP", "CMP", "PEI", "CMP", "DEC", "CMP", "CLD", "CMP", "PHX", "STP", "JML", "CMP", "DEC", "CMP",
	"CPX", "SBC", "SEP", "SBC", "CPX", "SBC", "INC", "SBC", "INX", "SBC", "NOP", "XBA", "CPX", "SBC", "INC", "SBC",
	"BEQ", "SBC", "SBC", "SBC", "PEA", "SBC", "INC", "SBC", "SED", "SBC", "PLX", "XCE", "JSR", "SBC", "INC", "SBC"
};
An array for the 256 opcodes.

Code: Select all

typedef enum ADDR_MODE {
	NONE = 0,                     //> NONE, Placeholder                 //            // Non-existant. Just a placeholder
	ABSA    ,                     //> Absolute   		                //    a       // 3.5.1  in WDC DataSheet
    JAII    ,                     //> Absolute Indexed Indirect Jump    //    (a,x)   // 3.5.2  in WDC DataSheet
	ABIX    ,                     //> Absolute Indexed with X           //    a,x     // 3.5.3  in WDC DataSheet
    ABIY    ,                     //> Absolute Indexed with Y           //    a,y     // 3.5.4  in WDC DataSheet	
	ABSI    ,                     //> Absolute Indirect                 //    (a)     // 3.5.5  in WDC DataSheet
	ALIX    ,                     //> Absolute Long Indexes with X      //    al,x    // 3.5.6  in WDC DataSheet
	ABSL    ,                     //> Absolute Long                     //    al      // 3.5.7  in WDC DataSheet
	ACCU    ,                     //> Accumulator                       //    A       // 3.5.8  in WDC DataSheet
	BLKM    ,                     //> Block Move                        //    xyc     // 3.5.9  in WDC DataSheet
	DIIX    ,                     //> Direct Indexed Indirect           //    (d,x)   // 3.5.10 in WDC DataSheet
	DINX    ,                     //> Direct Indexed with X             //    d,x     // 3.5.11 in WDC DataSheet
	DINY    ,                     //> Direct Indexed with Y             //    d,y     // 3.5.12 in WDC DataSheet
	DIIN    ,                     //> Direct Indirect Indexed           //    (d),y   // 3.5.13 in WDC DataSheet
	DILI    ,                     //> Direct Indirect Long Indexed      //    [d],y   // 3.5.14 in WDC DataSheet
	DILO    ,                     //> Direct Indirect Long              //    [d]     // 3.5.15 in WDC DataSheet
	DIRI    ,                     //> Direct Indirect                   //    (d)     // 3.5.16 in WDC DataSheet
	DIRE    ,                     //> Direct                            //    d       // 3.5.17 in WDC DataSheet
	IMME    ,                     //> Immediate                         //    #       // 3.5.18 in WDC DataSheet  2 bytes or 3 depending on MX....
	                                                                                  //                          0000 1001 3 bytes if M = 0 else 2
																					  //						  0010 1001
																					  //                          0100 1001
																					  //                          0110 1001
																					  //                          1000 1001
																					  //                          1010 1001
																					  //                          1100 1001
																					  //                          1110 1001
	                                                                                  //                          1010 0000 3 bytes if X = 0 else 2
																					  //                          1010 0010
																					  //                          1100 0000
																					  //                          1110 0000
																					  //                          1100 0010 2 bytes always
																					  //                          1110 0010
	IMPL    ,                     //> Implied                           //    i       // 3.5.19 in WDC DataSheet
	PCRL    ,                     //> Program Counter Relative Long     //    rl      // 3.5.20 in WDC DataSheet
	PCRE    ,                     //> Program Counter Relative          //    r       // 3.5.21 in WDC DataSheet
	STCK    ,                     //> Stack                             //    s       // 3.5.22 in WDC DataSheet
	SREL    ,                     //> Stack Relative                    //    d,s     // 3.5.23 in WDC DataSheet
	SRII    ,                     //> Stack Relative Indirect Indexed   //    (d,s),y // 3.5.24 in WDC DataSheet
} ADDR_MODE;
An enum containing all the addressing modes.

Code: Select all

static const ADDR_MODE OPCODE_ADDRESS_MODES[256] = {
//    0        1      2       3       4      5       6       7       8       9       A       B      C       D        E        F
    STCK,   DIIX,   STCK,   SREL,   DIRE,   DIRE,   DIRE,   DILO,   STCK,   IMME,   ACCU,   STCK,   ABSA,   ABSA,   ABSA,   ABSL,     // 0
    PCRE,   DIIN,   DIRI,   SRII,   DIRE,   DINX,   DINX,   DILI,   IMPL,   ABIY,   ACCU,   IMPL,   ABSA,   ABIX,   ABIX,   ALIX,     // 1
    ABSA,   DIIX,   ABSL,   SREL,   DIRE,   DIRE,   DIRE,   DILO,   STCK,   IMME,   ACCU,   STCK,   ABSA,   ABSA,   ABSA,   ABSL,     // 2
    PCRE,   DIIN,   DIRI,   SRII,   DINX,   DINX,   DINX,   DILI,   IMPL,   ABIY,   ACCU,   IMPL,   ABIX,   ABIX,   ABIX,   ALIX,     // 3
    STCK,   DIIX,   IMME,   SREL,   BLKM,   DIRE,   DIRE,   DILO,   STCK,   IMME,   ACCU,   STCK,   ABSA,   ABSA,   ABSA,   ABSL,     // 4
    PCRE,   DIIN,   DIRI,   SRII,   BLKM,   DINX,   DINX,   DILI,   IMPL,   ABIY,   STCK,   IMPL,   ABSL,   ABIX,   ABIX,   ALIX,     // 5
    STCK,   DIIX,   STCK,   SREL,   DIRE,   DIRE,   DIRE,   DILO,   STCK,   IMME,   ACCU,   STCK,   ABSI,   ABSA,   ABSA,   ABSL,     // 6
    PCRE,   DIIN,   DIRI,   SRII,   DINX,   DINX,   DINX,   DILI,   IMPL,   ABIY,   STCK,   IMPL,   JAII,   ABIX,   ABIX,   ALIX,     // 7
    PCRE,   DIIX,   PCRL,   SREL,   DIRE,   DIRE,   DIRE,   DILO,   IMPL,   IMME,   IMPL,   STCK,   ABSA,   ABSA,   ABSA,   ABSL,     // 8
    PCRE,   DIIN,   DIRI,   SRII,   DINX,   DINX,   DINY,   DILI,   IMPL,   ABIY,   IMPL,   IMPL,   ABSA,   ABIX,   ABIX,   ALIX,     // 9
    IMME,   DIIX,   IMME,   SREL,   DIRE,   DIRE,   DIRE,   DILO,   IMPL,   IMME,   IMPL,   STCK,   ABSA,   ABSA,   ABSA,   ABSL,     // a
    PCRE,   DIIN,   DIRI,   SRII,   DINX,   DINX,   DINY,   DILI,   IMPL,   ABIY,   IMPL,   IMPL,   ABIX,   ABIX,   ABIY,   ALIX,     // b
    IMME,   DIIX,   IMME,   SREL,   DIRE,   DIRE,   DIRE,   DILO,   IMPL,   IMME,   IMPL,   IMPL,   ABSA,   ABSA,   ABSA,   ABSL,     // c
    PCRE,   DIIN,   DIRI,   SRII,   STCK,   DINX,   DINX,   DILI,   IMPL,   ABIY,   STCK,   IMPL,   ABSI,   ABIX,   ABIX,   ALIX,     // D
    IMME,   DIIX,   IMME,   SREL,   DIRE,   DIRE,   DIRE,   DILO,   IMPL,   IMME,   IMPL,   IMPL,   ABSA,   ABSA,   ABSA,   ABSL,     // E
    PCRE,   DIIN,   DIRI,   SRII,   STCK,   DINX,   DINX,   DILI,   IMPL,   ABIY,   STCK,   IMPL,   JAII,   ABIX,   ABIX,   ALIX      // F
};
And then, another array containg the addresing mode for each opcode.

Each addresing mode has an associated length, and a printing format. Fixing PEA, PEI and PER it's as simple as adjusting it's addresing mode to the correct one. It's importante to note, this is just for the dissasembler, this is not used in the actual instruction emulation, though it may be a good idea at some point.

The above code still contains the wrong values for those three opcodes. And there may be others. As I implement each one of the opcodes, each one of them will be put to test, so I hope to catch any mistakes, sooner or later.

Cheers!
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: dromaius816 - a new 65816 emulator

Post by BigDumbDinosaur »

daniMolina wrote:
BigDumbDinosaur wrote:
The disassembly of PEA at $0006 and PEI at $0026 is incorrect.  Where are the operands?
Lost in the WDC datasheet I'm afraid.

In Table 5-5 Operation, Operation Codes, and Status Register, both PEA (opcode F4) and PEI (opcode D4) are listed as using the Stack, s addressing mode...

This business of using single letters to refer to registers is ambiguous, at best.  The data sheet is somewhat misleading, as none of the push instructions uses ‘s’ in the assembly language syntax.  What WDC means. but is not conveying very well, is PEA and PEI push words to the stack.  PEA is an immediate-mode instruction with a 16-bit operand, and PEI is a direct page-mode instruction with an 8-bit operand.

Despite the purported meaning of the mnemonic, PEA does not push an “effective address.”  What it pushes is a 16-bit, assembly-time constant that could be an address, or virtually anything else, such as the size of your bank account balance.  In the same vein, PEI does not engage in indirection.  What it pushes is the word found at the direct-page location that is PEI’s operand.  Again, that word could represent anything that can be expressed in 16 bits.

Incidentally, the Eyes & Lichty programming manual incorrectly describes the syntax of these instructions as PEA <operand> and PEI (<dp>).  The correct syntax is PEA #<operand> and PEI <dp>.  That incorrect syntax came from the first assembler (ORCA/M) that was written to target the 816.  Mike Westerfield, the assembler’s author, evidently misunderstood the effect of those instructions, a misunderstanding that was likely caused by the data sheet’s poor explanation of how they work.  David Eyes and Ron Lichty developed a lot of the material in their manual through the use of ORCA/M, thereby perpetuating the syntax.  The Kowalski assembler enforces PEA #<operand> and PEI <dp>, as does Supermon 816.

The other push instruction is PER, which pushes a word that is computed from the signed offset that is the instruction’s operand.  The syntax is PER <target>, in which <target> refers to a location in memory.  During assembly, the address of the instruction that follows PER will be subtracted from <target> via twos-complement arithmetic, generating a 16-bit, signed offset—the same method applies to computing the offset for BRL<target> must be somewhere in the execution bank, as the offset range will be -32768 to +32767.  At run time, the 816 will add PER’s operand to the address of the next instruction, using 16-bit, twos-complement arithmetic, and push the sum as a little-endian word that will be the run-time address of the <target> operand referenced in the source code.

As BRL and PER use a relative offset that can reach anywhere in a bank, they, along with the other relative branch instructions, facilitate the development of position-independent code.  In particular, a combination of BRL and PER may be used to synthesize a BSR (Branch to SubRoutine) instruction, which the 816 lamentably lacks:

Code: Select all

;branch to subroutine synthesis
;
         per ret_here-1        ;push effective return address
         brl function          ;call the subroutine
ret_here                       ;return here from subroutine

The above could also be written as:

Code: Select all

;branch to subroutine synthesis
;
         per *+5               ;push effective return address
         brl function          ;call the subroutine

In either case, relocation of the object code doesn’t affect operation.

Something else that PER is useful for is reporting the current execution address, e.g.:

Code: Select all

;where am I?
;
         rep #%00100000        ;16-bit accumulator
okayhere per okayhere          ;compute/push location
         pla                   ;will retrieve okayhere’s address

In many assemblers, the above could be written as:

Code: Select all

;where am I?
;
         rep #%00100000        ;16-bit accumulator
         per *                 ;compute/push location
         pla                   ;will retrieve the PER opcode’s address

Either way, it tells a program where it is running, should that matter in some way.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
daniMolina
Posts: 214
Joined: 25 Jan 2019
Location: Madrid, Spain

Re: dromaius816 - a new 65816 emulator

Post by daniMolina »

So...

PEA will be

Code: Select all

PEA #$1234
Which assembles to F4 34 12. On execution, $12 is pushed into the stack, then $34.

PEI will be

Code: Select all

PEI $20
The following code :

Code: Select all

LDA #$12
STA $21
LDA #$34
STA $20
PEI
With DP=0x0000, M=1 (M=1, to have 8bit loads into the Accumator, PEI is not affected by M), this will have the same result as above, $12 is pushed, then $34. According to the datasheet, this always pushes a word (2 bytes) into the stack, both in native and emulation modes. However, Andrew Jacobs emu816 seems to push only one byte when in emulation mode, which I think is not correct.

Finally, PER syntax will be:

Code: Select all

PER $1234
With the result of the offset between current PC and $1234 being pushed to the stack.

As far as I can tell, E, M and X do not affect PER, PEI and PEA in any way.

I've amended the disassembler with all these changes.
disassembler_fix.png
disassembler_fix.png (5.24 KiB) Viewed 3709 times
And again, thanks BDD for all your feedback. It is always a pleasure reading you.
User avatar
BigDumbDinosaur
Posts: 9425
Joined: 28 May 2009
Location: Midwestern USA (JB Pritzker’s dystopia)
Contact:

Re: dromaius816 - a new 65816 emulator

Post by BigDumbDinosaur »

daniMolina wrote:
So...

PEA will be

Code: Select all

PEA #$1234
Which assembles to F4 34 12. On execution, $12 is pushed into the stack, then $34.

Correct.

Quote:
PEI will be

Code: Select all

PEI $20
The following code :

Code: Select all

LDA #$12
STA $21
LDA #$34
STA $20
PEI
With DP=0x0000, M=1 (M=1, to have 8bit loads into the Accumator, PEI is not affected by M), this will have the same result as above, $12 is pushed, then $34.

Also correct.

Quote:
According to the datasheet, this always pushes a word (2 bytes) into the stack, both in native and emulation modes. However, Andrew Jacobs emu816 seems to push only one byte when in emulation mode, which I think is not correct.

PEI works the same in either operating mode, as do PEA and PER.

Quote:
Finally, PER syntax will be:

Code: Select all

PER $1234
With the result of the offset between current PC and $1234 being pushed to the stack.

“Current PC” meaning what?  The offset is computed relative to the address of the next instruction, not the value of PC at the PER opcode.  Assuming the instruction is PER $1234 and is assembled at $AB0000, the base address from which the offset will be computed will be $AB0003 and the offset will be $1231.  The disassembled instruction would appear as:

Code: Select all

AB0000  62 31 12     PER $1234

Quote:
As far as I can tell, E, M and X do not affect PER, PEI and PEA in any way.

Correct.

Quote:
I've amended the disassembler with all these changes.

Your disassembly of the PER instruction is incorrect.  It should be:

Code: Select all

F013: 62 D4 0F    PER $FFEA

The operand seen by the MPU when it executes the instruction will be $0FD4, not $FFEA.  In this case, the offset would be computed relative to $F016, which is the CLD instruction.

Also, note that disassembly should be displaying 24-bit addresses in the first column, even if operating in emulation mode.  For example:

Code: Select all

00F013  62 D8 0F    PER $FFEA

The 816 always emits a 24-bit address, regardless of mode.  Even when running in emulation mode, access to extended memory is possible, as instructions such as LDA $AB1234,X are perfectly acceptable, as is changing DB to some bank other than $00.  Also, doing a JML or JSL is legal in emulation mode, and will run code in an extended bank.

BTW, no need for the trailing colon in the assembly address field.
x86?  We ain't got no x86.  We don't NEED no stinking x86!
daniMolina
Posts: 214
Joined: 25 Jan 2019
Location: Madrid, Spain

Re: dromaius816 - a new 65816 emulator

Post by daniMolina »

BigDumbDinosaur wrote:
“Current PC” meaning what?  The offset is computed relative to the address of the next instruction, not the value of PC at the PER opcode.  Assuming the instruction is PER $1234 and is assembled at $AB0000, the base address from which the offset will be computed will be $AB0003 and the offset will be $1231.  The disassembled instruction would appear as:

Code: Select all

AB0000  62 31 12     PER $1234
"Current PC" meaning the PC by the time the last byte of the instruction has been read — but now I see that was both inaccurate and wrong, as that meant the last byte of the PER instruction. As a side effect, I also found that my code for calculating the offset for BRL was off by one byte due to pure laziness. I was adding 2 to the instruction address for 1-byte branches, and just reused the code for BRL.
BigDumbDinosaur wrote:
Also, note that disassembly should be displaying 24-bit addresses in the first column, even if operating in emulation mode.  For example:

Code: Select all

00F013  62 D8 0F    PER $FFEA
The 816 always emits a 24-bit address, regardless of mode.  Even when running in emulation mode, access to extended memory is possible, as instructions such as LDA $AB1234,X are perfectly acceptable, as is changing DB to some bank other than $00.  Also, doing a JML or JSL is legal in emulation mode, and will run code in an extended bank.

BTW, no need for the trailing colon in the assembly address field.
Noted, and changed. Keep in mind I am expanding the original emulator, which was a PET emulator only. There’s a lot of "8bit-ness" scattered throughout the code. Changing the memory panel and the disassembler is only a matter of tweaking a couple of "printf"s. Being able to add more than 64KB of memory to the emulated system will have to wait for now. This will involve creating some glue logic to capture the Bank Address due to how the emulator is implemented:

- The chips are emulated. The CPU, memory, clock, and many others (there's a 6522 in there that I haven't tried yet) are emulated with a lot of IFs and SWITCHes. No trace of the physical implementation of the real chips.
- However, the connections between ICs are simulated (a digital simulation, comparable to Logisim, I’d say). So yes, latching the BA is a real need here, much like it is in a real system.

It may be a bit too much for an emulated computer, but as my goal is to ultimately build what I am trying to emu/simulate, I feel it will be extremely useful.

All fixed now!
disassembler_fix_2.png
disassembler_fix_2.png (5.1 KiB) Viewed 3675 times
Post Reply