6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Thu Nov 21, 2024 9:27 pm

All times are UTC




Post new topic Reply to topic  [ 19 posts ]  Go to page Previous  1, 2
Author Message
PostPosted: Wed Feb 22, 2023 2:59 pm 
Offline
User avatar

Joined: Wed Feb 14, 2018 2:33 pm
Posts: 1488
Location: Scotland
strik wrote:
@drogon, are you aware that your union and its usage assumes a little endian machine on which this code is running?


What? There are big-endian systems out there? Who'd have thought. Amazing.

The rest is left as an exercise to the user.

-Gordon

_________________
--
Gordon Henderson.
See my Ruby 6502 and 65816 SBC projects here: https://projects.drogon.net/ruby/


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 22, 2023 3:34 pm 
Offline
User avatar

Joined: Tue Nov 16, 2010 8:00 am
Posts: 2353
Location: Gouda, The Netherlands
Another option for the V flag is to just ignore it, and instead make a note that it was possibly changed. Only when you actually see a PHP/BVC/BVS instruction then you go back to the original arguments and calculate the flag.


Top
 Profile  
Reply with quote  
PostPosted: Wed Feb 22, 2023 4:06 pm 
Offline
User avatar

Joined: Fri Aug 03, 2018 8:52 am
Posts: 746
Location: Germany
make a program for a real 65c02, generate all possible states of the V flag for all inputs. (17-bit input, 1-bit output, means the table would be exactly 16kB in size), then just print it out as hex to a terminal on your PC, copy it, and paste it in a C source file as an array.

ta-da! now you have a (pretty large) look up table where you just give it both inputs and the carry, and it will generate the correct V flag value.

alternatively, here is how my Emulator does Binary ADC:

Code:
// Macro to handle the Z and N Flags
#define SETNZ(val)      writeflag(CPU, N_FL, (val) & 128U); writeflag(CPU, Z_FL, !(val))

uint16_t tmp16;     // Temporary Result
uint8_t tmpin;      // Input value (from Memory)

tmp16 = (uint16_t)A + tmpin + (readflag(CPU, C_FL) ? 1 : 0);    // Add A, the input value, and the carry together
writeflag(CPU, C_FL, tmp16 >> 8);                               // Set the carry if the upper byte of tmp16 is not 0
writeflag(CPU, V_FL, ((~(A ^ tmpin)) & (A ^ tmp16)) & 0x80);    // Set the V Flag (no idea what this does but it works, so thanks drogon!)
A = (uint8_t)tmp16;                                             // put the low byte of tmp16 into A
SETNZ(A);                                                       // And set the Z and N Flags accordingly


honestly i recommend handling the reading and writing of flags in seperate functions or macros. i find it so much easier to work with.


Top
 Profile  
Reply with quote  
PostPosted: Wed May 03, 2023 1:58 pm 
Offline
User avatar

Joined: Sat Dec 01, 2018 1:53 pm
Posts: 730
Location: Tokyo, Japan
drogon wrote:
The overflow (V) flag is a somewhat tricky one and often mis-understood.

The flags in general can be quite tricky, but they all have equations you can just run. I don't know if anybody ever specified them for 6502, but Motorola did so in the manual for the 6800. For example, the first part of the page for ADC is as follows:

Attachment:
m6800-refman-adc.jpg
m6800-refman-adc.jpg [ 85.6 KiB | Viewed 2826 times ]

Thus, in my implementation (in Python), the addition code is as follows. (m is the "machine," the current state of the CPU.)

Code:
def isneg(b, signbit=7):
    sign = b & (1 << signbit)
    return 0 !=  sign

def iszero(b):
    return b == 0

def incbyte(byte, addend):
    ''' Return 8-bit `byte` incremented by `addend` (which may be negative).
        This returns an 8-bit unsigned result, wrapping at $FF/$00.
    '''
    return (byte + addend) & 0xFF

def add(m, augend, addend, carry=0):
    ''' Return the modular 8-bit sum of adding `addend` (the operand) and
        `carry` to `augend` (the contents of the register). Set H, N, Z, V
        and C flags based on the result, per pages A-4 (ADC) and A-5 (ADD)
        in the PRG.
    '''
    sum = incbyte(augend, addend)
    sum = incbyte(sum, carry)

    m.N = isneg(sum)
    m.Z = iszero(sum)

    bit7 = 0b10000000;              bit3 = 0b1000
    x7 = bool(augend & bit7);       x3 = bool(augend & bit3)
    m7 = bool(addend & bit7);       m3 = bool(addend & bit3)
    r7 = bool(sum & bit7);          r3 = bool(sum & bit3)

    #   The following is copied directly from PRG pages A-4 and A-5.
    m.C = x7 and m7  or  m7 and not r7  or  not r7 and x7
    m.H = x3 and m3  or  m3 and not r3  or  not r3 and x3
    m.V = x7 and m7 and not r7  or  not x7 and not m7 and r7

    return sum

As you can see, I basically just copied the equations straight out of the manual and into my code.

The above is the generic version; implementing the add without carry (ADD) and with carry (ADC) versions for each addressing mode is just a line each:

Code:
def adda(m):    m.a = add(m, m.a, readbyte(m))
def addaz(m):   m.a = add(m, m.a, m.mem[readbyte(m)])
def addam(m):   m.a = add(m, m.a, m.mem[readword(m)])
def addax(m):   m.a = add(m, m.a, m.mem[readindex(m)])
def addb(m):    m.b = add(m, m.b, readbyte(m))
def addbz(m):   m.b = add(m, m.b, m.mem[readbyte(m)])
def addbm(m):   m.b = add(m, m.b, m.mem[readword(m)])
def addbx(m):   m.b = add(m, m.b, m.mem[readindex(m)])

def adca(m):    m.a = add(m, m.a, readbyte(m), m.C)
...

That said, because I'm paranoid, I wrote full test coverage for all instructions anyway. This was actually pretty educational because it forced me to work out every possible case myself, and so gave me a much better understanding of the flags than I'd had before, particularly the V flag. I must admit that this was really more me assuming that the code was correct and tweaking the tests until they matched the code, but I may have found a bug or two in the code by analysing what was going wrong when a test vector didn't produce the results I'd expected. But more often it was because I'd gotten the test vector wrong.

Code:
ADDtests = (
    'arg0, arg1,  res, H, N, Z, V, C',
    (0x00, 0x00, 0x00, 0, 0, 1, 0, 0),
    (0x00, 0xFF, 0xFF, 0, 1, 0, 0, 0),
    #   H: half carry determined by bits 3
    (0x0E, 0x01, 0x0F, 0, 0, 0, 0, 0),
    (0x0F, 0x01, 0x10, 1, 0, 0, 0, 0),
    (0x10, 0x01, 0x11, 0, 0, 0, 0, 0),
    #   V: overflow determined by bits 6
    (0x7E, 0x01, 0x7F, 0, 0, 0, 0, 0),
    (0x7F, 0x01, 0x80, 1, 1, 0, 1, 0),
    (0x80, 0x01, 0x81, 0, 1, 0, 0, 0),
    #   C: carry determined by bits 7
    (0xFE, 0x01, 0xFF, 0, 1, 0, 0, 0),
    (0xFF, 0x01, 0x00, 1, 0, 1, 0, 1),
    (0xF0, 0x20, 0x10, 0, 0, 0, 0, 1),  # no half carry
    #   C and V
    (0x40, 0xF0, 0x30, 0, 0, 0, 0, 1),  # 2's comp: subtract from positive
    (0xF0, 0xF0, 0xE0, 0, 1, 0, 0, 1),  # 2's comp: subtract from negative
    (0x80, 0x80, 0x00, 0, 0, 1, 1, 1),
)

@tc(*ADDtests)
def test_ADD_C0(arg0, arg1, res, H, N, Z, V, C):
    runbinary('ADD', arg0, arg1, res, N, Z, V, C, H=H, inflags=R(C=0))

@tc(*ADDtests)
def test_ADD_C1(arg0, arg1, res, H, N, Z, V, C):
    runbinary('ADD', arg0, arg1, res, N, Z, V, C, H=H, inflags=R(C=1))

@tc(*ADDtests)
def test_ADC_C0(arg0, arg1, res, H, N, Z, V, C):
    runbinary('ADC', arg0, arg1, res, N, Z, V, C, H=H, inflags=R(C=0))

(The @tc(*ADDtests) bit above just runs the test immediately below it once for each test vector in ADDtests. There are 14 test vectors in it, so the code above runs a total of 42 unit tests.)

_________________
Curt J. Sampson - github.com/0cjs


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 19 posts ]  Go to page Previous  1, 2

All times are UTC


Who is online

Users browsing this forum: No registered users and 9 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: