I need help from someone here who has written an Apple II emulator (or else
knows a lot about how it works) and knows how to program in HyperTalk. I'm
writing an Apple II emulator, and when I run it I get into an infinite loop.
Here is what I found out the emulated processor was doing:
$0000- BRK
$C803- CLV
$C804- PHA
$C805- PHX
$C806- TSX
$C807- PLA
$C808- PLA
$C809- PLA
$C80A- TXS
$C80B- PHY
$C80C- LDX $C066
$C80F- LDY $C067
$C812- CLD
$C813- AND #$10
$C815- CMP #$10
$C817- LDA $C018
$C81A- AND $C01C
$C81D- AND #$80
$C81F- BEQ +5 <<-- this in particular is what doesn't look right
$0000- BRK <<-- BEQ instruction jumps to $0000 ?????
Below I have my HyperTalk script for the processor emulation.
Also, is there information somewhere that would tell someone who doesn't
know C or C++ how to emulate game I/O, vertical blanking, disk images, mouse
and clock cards, etc.? Does game I/O and vertical blanking require counting
clock cycles or something? What I/O memory locations do you use to control
the disk drive, get the mouse coordinates, and so on?
--
HyperTalk script:
Note: mpeek and mpoke are functions I put in the Apple II MMU script, which
is seperate
--
-- Carbon Dioxide 65C02 Emulator Core
-- version 1.0
-- © 2001 Kreative Software, a division of Kreative Korp.
-- http://kreativekorp.cjb.net
--
on sixfiveohtwo
-- this is the handler that emulates the 65C02 processor
global C02_A, C02_X, C02_Y, C02_S, C02_P, C02_PC
put C02_A into oldA
put C02_X into oldX
put C02_Y into oldY
put decodeInstruction(d2h(mpeek(C02_PC),2)) into theInst
add 1 to C02_PC
put d2b(C02_P) into f
put (char 1 of f is "1") into pfNegative
put (char 2 of f is "1") into pfOverflow
put (char 3 of f is "1") into pfWhatever
put (char 4 of f is "1") into pfBreak
put (char 5 of f is "1") into pfDecimal
put (char 6 of f is "1") into pfInterrupt
put (char 7 of f is "1") into pfZero
put (char 8 of f is "1") into pfCarry
put empty into operand
if "MM" is in word 2 of theInst then
put mpeek(C02_PC) + (256*mpeek(C02_PC + 1)) into operand
add 2 to C02_PC
else if "M" is in word 2 of theInst then
put mpeek(C02_PC) into operand
add 1 to C02_PC
else if word 2 of theInst = "#N" then
put "#" & mpeek(C02_PC) into operand
add 1 to C02_PC
end if
if "M" is in word 2 of theInst then put decodeAddrMode(word 2 of
theInst,operand) into operand
delete word 2 of theInst
-- at this moment, theInst contains the instruction mneumonic (PLA, STX,
BRK, etc)
-- and operand contains a memory address or a number (with "#" before it)
or nothing
-- doing the easy stuff first :)
if char 1 to 2 of theInst = "cl" then changeFlagState (char 3 of
theInst),false
if char 1 to 2 of theInst = "se" then changeFlagState (char 3 of
theInst),true
if theInst = "tax" then put C02_A into C02_X
if theInst = "tay" then put C02_A into C02_Y
if theInst = "tsx" then put C02_S into C02_X
if theInst = "txs" then put C02_X into C02_S
if theInst = "txa" then put C02_X into C02_A
if theInst = "tya" then put C02_Y into C02_A
if theInst = "pha" then pushStack C02_A
if theInst = "pla" then put popStack() into C02_A
if theInst = "phx" then pushStack C02_X
if theInst = "plx" then put popStack() into C02_X
if theInst = "phy" then pushStack C02_Y
if theInst = "ply" then put popStack() into C02_Y
if theInst = "php" then pushStack C02_P
if theInst = "plp" then put popStack() into C02_P
if theInst = "lda" then put operandValue(operand) into C02_A
if theInst = "ldx" then put operandValue(operand) into C02_X
if theInst = "ldy" then put operandValue(operand) into C02_Y
if theInst = "sta" then mpoke operand,C02_A
if theInst = "stx" then mpoke operand,C02_X
if theInst = "sty" then mpoke operand,C02_Y
if theInst = "inx" then add 1 to C02_X
if theInst = "iny" then add 1 to C02_Y
if theInst = "dex" then subtract 1 from C02_X
if theInst = "dey" then subtract 1 from C02_Y
if theInst = "bcc" and not pfCarry then put operand into C02_PC
if theInst = "bcs" and pfCarry then put operand into C02_PC
if theInst = "beq" and pfZero then put operand into C02_PC
if theInst = "bmi" and pfNegative then put operand into C02_PC
if theInst = "bne" and not pfZero then put operand into C02_PC
if theInst = "bpl" and not pfNegative then put operand into C02_PC
if theInst = "bvc" and not pfOverflow then put operand into C02_PC
if theInst = "bvs" and pfOverflow then put operand into C02_PC
if theInst = "bra" or theInst = "jmp" then put operand into C02_PC
if theInst = "inc" and operand is empty then add 1 to C02_A
if theInst = "dec" and operand is empty then subtract 1 from C02_A
if theInst = "and" then put binLogic(operandValue(operand),C02_A,"and")
into C02_A
if theInst = "eor" then put binLogic(operandValue(operand),C02_A,"xor")
into C02_A
if theInst = "ora" then put binLogic(operandValue(operand),C02_A,"or")
into C02_A
if theInst is in "asl lsr rol ror" and operand is empty then put
bitshift(C02_A,theInst) into C02_A
if theInst is in "asl lsr rol ror" and operand is not empty then mpoke
operand,bitshift(mpeek(operand),theInst)
-- now we get to the harder stuff
if theInst = "inc" and operand is not empty then
put mpeek(operand)+1 into v
if v > 255 then
subtract 256 from v
changeFlagState "v",true
end if
mpoke operand,v
end if
if theInst = "dec" and operand is not empty then
put mpeek(operand)-1 into v
if v < 0 then
add 256 to v
changeFlagState "v",true
end if
mpoke operand,v
end if
if theInst = "jsr" then
put C02_PC - 1 into a
pushStack (a DIV 256)
pushStack (a MOD 256)
put operand into C02_PC
end if
if theInst = "rts" then
put popStack() into a
put popStack() into b
put (256*b)+a+1 into C02_PC
end if
if theInst = "brk" then
pushStack (C02_PC DIV 256)
pushStack (C02_PC MOD 256)
pushStack C02_P
changeFlagState "b",true
put mpeek(65534) + (256*mpeek(65535)) into C02_PC -- interrupt vector
end if
if theInst = "rti" then
put popStack() into C02_P
put popStack() into a
put popStack() into b
put (256*b)+a into C02_PC
end if
if theInst = "bit" then
put operandValue(operand) into x
if x > 127 then
subtract 128 from x
changeFlagState "n",true
else changeFlagState "n",false
if x > 63 then
changeFlagState "v",true
else changeFlagState "v",false
end if
if theInst = "adc" then
put operandValue(operand) into x
add x to C02_A
if C02_A > 255 then
subtract 256 from C02_A
changeFlagState "v",true
end if
end if
if theInst = "sbc" then
put operandValue(operand) into x
subtract x from C02_A
if C02_A < 0 then
add 256 to C02_A
changeFlagState "v",false
end if
end if
if theInst = "cmp" then
put operandValue(operand) into x
put C02_A into y
changeFlagState "z",(x=y)
changeFlagState "n",(not (x>y))
end if
if theInst = "cpx" then
put operandValue(operand) into x
put C02_X into y
changeFlagState "z",(x=y)
changeFlagState "n",(not x>y)
end if
if theInst = "cpy" then
put operandValue(operand) into x
put C02_Y into y
changeFlagState "z",(x=y)
changeFlagState "n",(not x>y)
end if
if theInst is not in "cmp cpx cpy" then
-- the above instructions are the ones exempt from neg/zero check
-- instructions that don't change accumulator or indexes need not
-- be exempted, since the following would never then execute
if C02_A <> oldA then
changeFlagState "n",(C02_A>127)
changeFlagState "z",(C02_A=0)
end if
if C02_X <> oldX then
changeFlagState "n",(C02_X>127)
changeFlagState "z",(C02_X=0)
end if
if C02_Y <> oldY then
changeFlagState "n",(C02_Y>127)
changeFlagState "z",(C02_Y=0)
end if
end if
changeFlagState "v",false
if C02_A > 255 then
subtract 256 from C02_A
changeFlagState "v",true
end if
if C02_X > 255 then
subtract 256 from C02_X
changeFlagState "v",true
end if
if C02_Y > 255 then
subtract 256 from C02_Y
changeFlagState "v",true
end if
if C02_A < 0 then
add 256 to C02_A
changeFlagState "v",true
end if
if C02_X < 0 then
add 256 to C02_X
changeFlagState "v",true
end if
if C02_Y < 0 then
add 256 to C02_Y
changeFlagState "v",true
end if
if C02_S > 255 then subtract 256 from C02_S
if C02_S < 0 then add 256 to C02_S -- stack overflow, but we don't seem to
care
if C02_PC > 65535 then subtract 65536 from C02_PC -- probably another
thing that would normally cause a crash
end sixfiveohtwo
function decodeInstruction h
-- this decodes the 65C02 opcode!
if char 2 of h is in "3B" then return "NOP N/A"
put offset(char 1 of h,"123456789ABCDEF") into a
if char 2 of h is in "159D" then
if h = "89" then return "bit #N" -- only exception to the pattern
put item ((a DIV 2) + 1) of "ora,and,eor,adc,sta,lda,cmp,sbc" into r
put offset(char 2 of h,"159D")*2 into b
if (a/2) = (a DIV 2) then subtract 1 from b
put space & word b of "(M,X) (M),Y M M,X #N MM,Y MM MM,X" after r
return r
end if
if char 2 of h is in "7F" then
put char (a DIV 8)+1 of "rs" into r
if char 2 of h = "7" then
put "mb" after r
else put "bb" before r
put (a MOD 8) after r
if char 2 of h = "7" then put " M" after r
if char 2 of h = "F" then put " +/-M" after r
return r
end if
if char 2 of h is "8" then return (item a+1 of
"php,clc,plp,sec,pha,cli,pla,sei,dey,tya,tay,clv,iny,cld,inx,sed") && "N/A"
if char 2 of h is "A" then return (item a+1 of
"asl,inc,rol,dec,lsr,phy,ror,ply,txa,txs,tax,tsx,dex,phx,nop,plx") && "N/A"
if char 2 of h is in "6E" then
if h = "9E" then return "stz MM,X" -- only exception to the pattern
put (item ((a DIV 2) + 1) of "asl,rol,lsr,ror,stx,ldx,dec,inc") && "M"
into r
if char 2 of h = "E" then put "M" after r
if (a/2) = (a DIV 2) then return r
if (char 2 of h is "6") then return r & comma & char ((a+1)/2) of
"XXXXYYXX"
if (char 2 of h is "E") then return r & comma & char ((a+1)/2) of
"XXXXXYXX"
end if
if char 2 of h = "2" then
put item a+1 of
"nop,ora,nop,and,nop,eor,nop,adc,nop,sta,ldx,lda,nop,cmp,nop,sbc" into r
if r = "nop" then return r && "N/A"
if r = "ldx" then return r && "#N"
return r && "(M)"
end if
if char 2 of h = "4" then
put item a+1 of
"tsb,trb,bit,bit,nop,nop,stz,stz,sty,sty,ldy,ldy,cpy,nop,cpx,nop" into r
return r && word a+1 of "M M M M,X N/A N/A M M,X M M,X M M,X M N/A M
N/A"
end if
if char 2 of h = "C" then
put item a+1 of
"tsb,trb,bit,bit,jmp,nop,jmp,jmp,sty,stz,ldy,ldy,cpy,nop,cpx,nop" into r
return r && word a+1 of "MM MM MM MM,X MM N/A (MM) (MM,X) MM MM MM MM,X
MM N/A MM N/A"
end if
if char 2 of h = "0" then
put item a+1 of
"brk,bpl,jsr,bmi,rti,bvc,rts,bvs,bra,bcc,ldy,bcs,cpy,bne,cpx,beq" into r
if (a/2) <> (a DIV 2) then return r && "+/-M"
return r && word (a/2)+1 of "N/A MM N/A N/A +/-M #N #N #N"
end if
end decodeInstruction
function decodeAddrMode origMA,addrMode
global C02_X, C02_Y, C02_PC
if addrMode = "M" or addrMode = "MM" then return origMA -- absolute
if addrMode = "(M,X)" or addrMode = "(MM,X)" then return
mpeek(origMA+C02_X)+(256*mpeek(origMA+C02_X+1)) -- indexed indirect
if addrMode = "(M,Y)" or addrMode = "(MM,Y)" then return
mpeek(origMA+C02_Y)+(256*mpeek(origMA+C02_Y+1)) -- indexed indirect
if addrMode = "(M),X" or addrMode = "(MM),X" then return
mpeek(origMA)+(256*mpeek(origMA+1))+C02_X -- indirect indexed
if addrMode = "(M),Y" or addrMode = "(MM),Y" then return
mpeek(origMA)+(256*mpeek(origMA+1))+C02_Y -- indirect indexed
if addrMode = "M,X" or addrMode = "MM,X" then return origMA+C02_X --
absolute indexed
if addrMode = "M,Y" or addrMode = "MM,Y" then return origMA+C02_Y --
absolute indexed
if addrMode = "(M)" or addrMode = "(MM)" then return
mpeek(origMA)+(256*mpeek(origMA+1)) -- indirect
if addrMode = "+/-M" or addrMode = "+/-MM" then
-- relative
put origMa into r
if r > 127 then subtract 256 from r
return r + C02_PC
-- may have to adjust a little afterwards if the program counter is off,
though
end if
end decodeAddrMode
on changeFlagState x,y
global C02_P
put "nvwbdizc" into flagcodes
put d2b(C02_P) into b
if y then
put "1" into char offset(x,flagcodes) of b
else put "0" into char offset(x,flagcodes) of b
put b2d(b) into C02_P
end changeFlagState
function getFlagState x
global C02_P
put "nvwbdizc" into flagcodes
put d2b(C02_P) into b
return (char offset(x,flagcodes) of b is "1")
end getFlagState
on pushStack x
global C02_S, C02_Running
mpoke (256+C02_S),x
subtract 1 from C02_S
if C02_S < 0 then
-- ooops!
answer "Warning! Warning! 65C02 stack crashed into zero page. Should I
continue?" with "Yes" or "No"
if it is "No" then
put false into C02_Running -- if using this variable for emulation
control
exit to hypercard
else put 255 into C02_S
end if
end pushStack
function popStack x
global C02_S
put mpeek(257+C02_S) into v
add 1 to C02_S
if C02_S > 255 then put 255 into C02_S -- not so serious, I think
return v
end popStack
function bitshift val,typ
-- use this to deal with asl, lsr, rol, and ror
if typ = "asl" then
put val*2 into x
if x > 255 then subtract 256 from x
return x
else if typ = "lsr" then
put val DIV 2 into x
return x
else if typ = "rol" then
put val*2 into x
if getFlagState("c") then add 1 to x
if x > 255 then
subtract 256 from x
changeFlagState "c",true
else changeFlagState "c",false
return x
else if typ = "ror" then
put val/2 into x
if getFlagState("c") then add 128 to x
if x <> (x DIV 1) then
subtract .5 from x
changeFlagState "c",true
else changeFlagState "c",false
return x
end if
end bitshift
function binlogic val,valu,typ
-- use this to deal with and, eor, ora, and other logic functions
put d2b(val) into a
put d2b(valu) into b
put empty into r
if char 1 of typ = "n" then
delete char 1 of typ
put "0" into rv
else put "1" into rv
repeat with x = 1 to 8
put char x of a into c
put char x of b into d
put "0" into char x of r
if rv="0" then put "1" into char x of r
if typ="and" and c="1" and d="1" then put rv into char x of r
if typ="or" and (c="1" or d="1") then put rv into char x of r
if typ="xor" and c="1" and d="0" then put rv into char x of r
if typ="xor" and d="1" and c="0" then put rv into char x of r
end repeat
return b2d(r)
end binlogic
function operandValue a
if char 1 of a = "#" then
delete char 1 of a
return a
else return mpeek(a)
end operandValue
on hitResetPin
-- starts up the 65C02 processor
global C02_A,C02_X,C02_Y,C02_S,C02_P,C02_PC
put 0 into C02_A
put 0 into C02_X
put 0 into C02_Y
put 255 into C02_S
put 0 into C02_P
put mpeek(65532) + (256*mpeek(65533)) into C02_PC -- reset vector
end hitResetPin
on hitIRQpin
if getFlagState("i") then exit hitIRQpin
pushStack (C02_PC DIV 256)
pushStack (C02_PC MOD 256)
pushStack C02_P
changeFlagState("b",false)
put mpeek(65534) + (256*mpeek(65535)) into C02_PC -- interrupt vector
end hitIRQpin
on hitNMIpin
pushStack (C02_PC DIV 256)
pushStack (C02_PC MOD 256)
pushStack C02_P
put mpeek(65530) + (256*mpeek(65531)) into C02_PC -- nmi vector
end hitNMIpin
function d2h x, r
put empty into h
repeat r
put x MOD 16 into y
put x DIV 16 into x
put char (y+1) of "0123456789ABCDEF" before h
end repeat
return h
end d2h
function h2d x
put 0 into r
repeat with n = the number of chars in x down to 1
put (the number of chars in x) - n into p
put offset(char n of x,"123456789ABCDEF") into z
add (z*(16^p)) to r
end repeat
return r
end h2d
function d2b x
put empty into r
repeat with c = 7 down to 0
if x > (2^c) or x = (2^c) then
put "1" after r
subtract (2^c) from x
else put "0" after r
end repeat
return r
end d2b
function b2d x
put 0 into r
repeat with n = 1 to the number of chars in x
put (the number of chars in x) - n into p
if char n of x = "1" then add (2^p) to r
end repeat
return r
end b2d
function disasmLine addr
put addr into VirtualPC -- :) virtual program counter
put decodeInstruction(d2h(mpeek(VirtualPC),2)) into theInst
add 1 to VirtualPC
put empty into operand
if word 2 of theInst = "+/-M" then
put mpeek(VirtualPC) into operand
if operand > 127 then subtract 256 from operand
if operand > -1 then put "+" before operand
put operand into word 2 of theInst
add 1 to VirtualPC
else if "MM" is in word 2 of theInst then
put offset("MM",word 2 of theInst) into f
put mpeek(VirtualPC) + (256*mpeek(VirtualPC + 1)) into operand
put ("$" & d2h(operand,4)) into char f to (f+1) of word 2 of theInst
add 2 to VirtualPC
else if "M" is in word 2 of theInst then
put offset("M",word 2 of theInst) into f
put mpeek(VirtualPC) into operand
put ("$" & d2h(operand,2)) into char f of word 2 of theInst
add 1 to VirtualPC
else if word 2 of theInst = "#N" then
put mpeek(VirtualPC) into operand
put ("#$" & d2h(operand,2)) into char 2 of word 2 of theInst
if char 2 of word 2 of theInst = "#" then delete char 2 of word 2 of
theInst
add 1 to VirtualPC
else if word 2 of theInst = "N/A" then
delete word 2 of theInst
end if
return theInst & return & VirtualPC
end disasmLine
-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----== Over 80,000 Newsgroups - 16 Different Servers! =-----
Update:
I fixed some of the problems I was having (swapped parameters on the
DecodeAddrMode function and checking for overflow _after_ checking for
zero/negative instead of before). I now have it down to a smaller loop:
$D511- C8 INY
$D512- B1 5E LDA ($5E),Y
$D514- D0 FB BNE -5
$D511- C8 INY
In ApplePC, calling this address [$D511], at least after a complete startup,
gets me an Applesoft prompt. In my emulator, it seems to be executing this
loop over and over again and I don't even have 'Apple //c' across the screen
yet.
-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----== Over 80,000 Newsgroups - 16 Different Servers! =-----