Login

Subversion Repositories NedoOS

Rev

Rev 1448 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

;
;65C02 emulator on a Z80
;by James Smith (UK), 2008
;
        DEVICE ZXSPECTRUM1024
        include "../_sdk/sys_h.asm"

;UNEXPANDED VIC20

;section below defines macros

;V1.01 - 2T off decoding loop and 4T off BRAnch instructions.
;V1.02 - 4T off ZP,X ; ZP,Y code
;V1.03 - fixed JSR (a,X) instruction. 11T off INX/DEX/INY/DEY instructions
;V1.04 - fixed PLP (Z flag lost). Modified TRB (CPL before AND now)
;v1.05 - redesigned ADC and SBC instructions to cope with BCD more quickly
;v1.06 - UK101 related
;v1.10 - improved 6502 core - using half of DR BEEP method
;v1.11 - UK101 related
;v1.12 - better way of setting Z flag - improved TRB,TSB,BIT and PHP instructions
;v1.13 - better way of setting V flag - improved ADC,SBC instructions
;v1.14 - fixed (indirect,X) addressing mode - missed out LD L,A after ADD A,L instruction
;v1.15 - 3T off BVC instruction. Improved keyscan routine - quicker and supports CTRL+C
;v1.16 - 7T off absolute,X ; abs,Y ; (ind),Y routines
;v1.17 - 4T off (indirect,X). Fix from v1.14 has been optimised.
;v1.18 - UK101 related
;v1.19 - 2T off PHP, 12T off RTI, 13T off PLP instructions
;v1.20 - 4T off abs,X ; abs,Y ; (ind),Y routines
;v1.21 - bug fixed in new PHP. 4T off (indirect,X). Illegal opcodes reduced to 3 bytes long
;v1.22 - branch instructions recoded. 7T/10T off each instruction
;v1.23 - 7T off JSR, 2T off BRK instructions
;v1.24 - UK101 related
;v1.25 - change refs from vYreg to IYH - effectively a test to see if it works
;v1.26 - change refs from vXreg to IYL. 4T off LDX/LDY instructions

;v1.41 - implemented $9120 (write), keyboard routines. Fixed bug in row 16+ of screendraw
;v1.42 - implemented F1/3/5/7 keys, cursor keys, RUNSTOP, left arrow, VIC table, Column width register
;        however cursor keys now stop normal SHIFT+5 to +8 which means you can't do '(' !
;V1.43 - 3K expanded RAM added. Charmap made read only now.
;v1.44 - 4T off TSX and TXS. Changes to code to make it more address independent.
;v1.45 - NMI implemented. Pound sign key defined. Charset pointer ($9005) implemented. Cursor keys removed.
;        tried to implement VIA1 IFR and IER - still no RUN/STOP+RESTORE break key.
;V1.50 - New memory maps specified - 8K VIC RAM now contiguous. Assembly less sensitive on location address
;v1.51 - New way of accessing VIC/VIA I/O addresses
;v1.52 - Fixed JR error in TRB,TSB. 2T off PLP, 4T off PHP instructions. Removed need for FFpage constant.
;v1.53 - Row depth register implemented. False border implemented to give indication of VIC screen size.
;v1.54 - Colour RAM implemented. Invert screen bit and background colour of $900F now implemented.
;v1.55 - redesigned colour ram/invert implementation. Now trap updates to colour ram and update colours
;        seperately from screen ram. Invert now done in ATTR stage rather than screen ram stage.
;        RUN/STOP+RESTORE now works as $912F and $911F are write mirrors of $9121 and $9111 and need to work.
;v1.56 - put bright colours into colour mapping table. Supported wraparound when chargen points to 7168
;        (codes 128+ wrap into ROM at $8000). Fixed bug in $9003 routine. Changed SUB $CE to SUB colourram.
;v1.57 - change to background colour now forces whole screen attr refresh. Unneeded SUB $F0 removed from $9005
;v1.58 - added support for joystick (via Kempston). $9003 routine modified to refresh screen when row depth
;        changed. Attribute refresh routines also take into account row depth now (didn't before). All access
;        for reading to VIA/VIC is via a jump table
;v1.59 - 10T off write traps and a further 11T off screenwrite. RUN/STOP key implemented.
;        Discovered that v1.58/9 are slower as VIC is always checking VIA1 911F (tape sense in IRQ routine - $EAEF)
;v1.60 - another core change. This time to memory read/write routines. 4T off each memory read/write (not M1 fetch)
;        also some changes to I/O routines as they have to set H on exit rather than A. FFrealpage re-implemented.
;V1.61 - Psuedo double-height chars routine implemented. Right joystick routine fixed.
;        9T off VICout routine as quicker method than PUSH BC/POP IX is used
;        Rowtable used in screenwrite routine - over 10T quicker now
;        Minor speed improvements in ATTR and VICout routines.
;v1.62 - Improvements to double-height routine made. Screenwrite DJNZ loop expanded out for increased speed.
;        $9004 (and $9003 bit 7) raster counter implemented. Implemented CBM key (SYM+C)
;v1.63 - Implemented SOUND ($900A-$900D). VIA T1 lsb emulated for randomness by using Z80 R register
;v1.64 - Improved reset routine to reset VIC display. Fixed JR bug in $900F routine.
;        3T off RAMread_trap routine. Stopped VICborder from going bright.
;v1.65 - 6T off CLI/SEI/CLD/SED. Speed improvements in VIAout, VIA2B, rVICsoundA/B/C. Fixed bug in RAMread_trap
;        causing character set changes to fail.
;v1.66 - Faster attr4 routine. Added SYM+6 for CURSOR DOWN and SYM+8 for CURSOR RIGHT. SYM+4 is HOME.
;v1.67 - Fault in paging tables. Unallocated memory should point to $02 not $00 - otherwise it corrupts
;        page $FF (top of Kernal ROM). Also fixed character ROM pointers. RAMread_trap faster as IOread will never
;        straddle 2 pages, so safe to use 8-bit arithmetic.
;v1.68 - Major fault in Writescreen fixed! When non-double height chars were in use, IX wasn't being reset back to
;        normal effectively causing the routine to be run twice. LISTing a BASIC program is now 20% quicker!! Also
;        changed limits of column width to 31 columns (was 27). EI at start relocated after 6502 has been initialised.
;v1.69 - assembly listing tidied up. Routines re-organised (couldn't make it 256 bytes shorter). Other tables moved
;        in memory to fit. All tables moved to end of code for easier changing in the future. refreshattr, NMI and
;        IRQ routines modified to be position independent. Slightly faster NMI and IRQ routines.
;v1.70 - Core change. The addressing modes which fetch 2 bytes after opcode (eg: absolute,X) were translating DE twice.
;        Now DE is only retranslated if page boundary is crossed via the nextbyte2 routine. JSR + JMP also changed.
;v1.71 - Further core changes. Now nextbyte has also been changed. Unfortunately the main decode loop is 4T slower as
;        the translated address msb needs to be stored for use by nextbyte.
;v1.72 - 32T off PLP! 3T off BVC and BVS instructions. BIT #imm fixed as it should only modify Z flag. Attr5/6 modified
;        to handle BRIGHT bit correctly. VIC9002 modified to support colourRAM at either $9400 or $9600.
;v1.73 - Flaw in PLP fixed, but now it's only -15T off. JMP (addr) changed to use correct macros (readtrap). Core changes
;        of 1.70 and 1.71 have been changed. Getting the opcode and following byte or two should be treat as "M1"
;        fetches. These aren't trapped only translated, so the code therefore never needs to CALL RAMread_trap. The main
;        decoding loop is 4T faster (back to how it was).
;v1.74 - Back to v1.72 core, but with revised m1trap routine.
;v1.75 - Back to v1.73 core but with revised "nextbyte2" macro and faster m1trap routine. Fix for ROL A and ROR A.
;v1.76 - Re-org of 6502 routines - 768 bytes shorter. Fixes for JMP (A,X) and BRK included.
;v1.77 - Keyboard scan routine modified to exit quickly if no keys are pressed and other minor speed improvements.
;v1.80 - core verification with C64 utilities. Changes : 1) PLP forces bit 5 to 1. 2) PHP/BRK force B flag set during
;        push. D mode can now be set via PLP.
;v1.81 - speed improvements to BCS/CC/PL/MI/VS/VC/EQ/NE as full nextbyte macro is only needed when branch is required.
;        22T saved when condition isn't met.
;v1.82 - Speed up for Ind,Y; abs,X and abs,Y instructions. Improvement to ADC.
;        Changed RETI to RET in interrupt routine. Better 'illegal' routines.
;v1.83 - Improvements to graphics routines.  Faster attr routines as coltrans table is page aligned. Screen now
;        allowed to be 24 rows high. Screen memory now allowed to be moved, enabling expanded memory support.
;v1.86 - At least 10T off PHP, 2T off PLP. SP moved to non-contended RAM. Attr5/6 shorter and faster. RAMtrap moved
;        into screenwrite routine saving 6T when writing to screen. Changes to bit 7 of $9002 now force screen
;        memory to be recalculated - this fixes many bugs.
;v1.87 - 3T off RTI. 4T off PLP (except when using RTI). Rewritten joystick routines. Writescreen 8/16 row code (got rid
;        of jp to enddisplay2 and row counting code optimised - use ADD instead of SBC). Several VIC routines also
;        changed to cope with this.
;v1.88 - speed up in attr4
;v1.89 - fixed PLP (4T slower). RTI -6T. ADD and SBC ops are shorter and 3T faster when overflow set. Rewritten sound
;        routines. BRK 3T quicker, 1 byte shorter.
;v1.90 - routines modified to use real AF for 6502 A and F registers. AF' used by Z80 routines. Imm + ZP modes don't
;        corrupt Z80 A-reg or flags. TSB/TRB/BIT recoded. Bug in screenshow when rows (9003)=0 fixed. VIA/VIC
;        trap code quicker. NMI/IRQ code changed. Various memory sizes implemented.

;#define DEFB .BYTE
;#define DW .WORD
;#define DEFM .TEXT
;#define ORG  .ORG
;#define EQU  .EQU
;#define equ  .EQU
;#define DB   .BYTE
;#define db   .BYTE

base            EQU $B400       ;THIS IS +512 FROM WHERE CODE STARTS!!

ZPrealpage      EQU $80
SPrealpage      EQU $81
FFrealpage      EQU $7F
chargen         EQU $A000
ScreenPage      EQU $9E
colourram       EQU $B0
;page 1 must ALWAYS be after page 0!

M1page          EQU (base+$2900)/256    ;was $DB
MRpage          EQU (base+$2A00)/256    ;was $DC ;read readdressing+1 for every HSB
MWpage          EQU (base+$2B00)/256    ;was $DD ;write readdressing+1 for every HSB
Oppage          EQU (base+$2800)/256    ;was $DA
IM2page         EQU (base+$2600)/256    ;was $D8
Safepage        EQU base+$1800
        ;display "end=",end

;all opcode routine addresses need to be
;defined before trying to assemble it
;otherwise the ORG base+x instructions won't make much sense

OP0     EQU base+$0     ;len=57
OP1     EQU base+$1701  ;len=42
OP2     EQU base+$1602  ;len=3
OP3     EQU base+$1503  ;len=3
OP4     EQU base+$1404  ;len=22
OP5     EQU base+$1605  ;len=20
OP6     EQU base+$1506  ;len=17
OP7     EQU base+$1307  ;len=3
OP8     EQU base+$1208  ;len=29
OP9     EQU base+$1109  ;len=17
OP10    EQU base+$130A  ;len=8
OP11    EQU base+$100B  ;len=3
OP12    EQU base+$F0C   ;len=37
OP13    EQU base+$E0D   ;len=35
OP14    EQU base+$100E  ;len=32
OP15    EQU base+$D0F   ;len=3
OP16    EQU base+$C10   ;len=21
OP17    EQU base+$B11   ;len=39
OP18    EQU base+$1312  ;len=34
OP19    EQU base+$D13   ;len=3
OP20    EQU base+$A14   ;len=23
OP21    EQU base+$915   ;len=25
OP22    EQU base+$D16   ;len=22
OP23    EQU base+$1517  ;len=3
OP24    EQU base+$818   ;len=5
OP25    EQU base+$1619  ;len=41
OP26    EQU base+$151A  ;len=7
OP27    EQU base+$141B  ;len=3
OP28    EQU base+$111C  ;len=38
OP29    EQU base+$81D   ;len=41
OP30    EQU base+$141E  ;len=38
OP31    EQU base+$71F   ;len=3
OP32    EQU base+$620   ;len=29
OP33    EQU base+$1521  ;len=42
OP34    EQU base+$722   ;len=3
OP35    EQU base+$523   ;len=3
OP36    EQU base+$424   ;len=32
OP37    EQU base+$1225  ;len=20
OP38    EQU base+$C26   ;len=17
OP39    EQU base+$727   ;len=3
OP40    EQU base+$528   ;len=49
OP41    EQU base+$329   ;len=17
OP42    EQU base+$72A   ;len=7
OP43    EQU base+$172B  ;len=3
OP44    EQU base+$D2C   ;len=47
OP45    EQU base+$A2D   ;len=35
OP46    EQU base+$172E  ;len=32
OP47    EQU base+$102F  ;len=3
OP48    EQU base+$E30   ;len=21
OP49    EQU base+$F31   ;len=39
OP50    EQU base+$1032  ;len=34
OP51    EQU base+$933   ;len=3
OP52    EQU base+$1334  ;len=37
OP53    EQU base+$735   ;len=25
OP54    EQU base+$936   ;len=22
OP55    EQU base+$C37   ;len=3
OP56    EQU base+$B38   ;len=4
OP57    EQU base+$1239  ;len=41
OP58    EQU base+$C3A   ;len=7
OP59    EQU base+$33B   ;len=3
OP60    EQU base+$B3C   ;len=53
OP61    EQU base+$63D   ;len=41
OP62    EQU base+$33E   ;len=38
OP63    EQU base+$23F   ;len=3
OP64    EQU base+$140   ;len=16
OP65    EQU base+$C41   ;len=45
OP66    EQU base+$1642  ;len=3
OP67    EQU base+$1143  ;len=3
OP68    EQU base+$1444  ;len=3
OP69    EQU base+$1645  ;len=20
OP70    EQU base+$1146  ;len=17
OP71    EQU base+$1447  ;len=3
OP72    EQU base+$E48   ;len=9
OP73    EQU base+$849   ;len=17
OP74    EQU base+$44A   ;len=8
OP75    EQU base+$154B  ;len=3
OP76    EQU base+$94C   ;len=17
OP77    EQU base+$24D   ;len=35
OP78    EQU base+$174E  ;len=32
OP79    EQU base+$154F  ;len=3
OP80    EQU base+$A50   ;len=24
OP81    EQU base+$E51   ;len=39
OP82    EQU base+$1552  ;len=37
OP83    EQU base+$753   ;len=3
OP84    EQU base+$1054  ;len=3
OP85    EQU base+$455   ;len=25
OP86    EQU base+$756   ;len=22
OP87    EQU base+$1157  ;len=3
OP88    EQU base+$1058  ;len=8
OP89    EQU base+$1659  ;len=41
OP90    EQU base+$135A  ;len=17
OP91    EQU base+$115B  ;len=3
OP92    EQU base+$F5C   ;len=3
OP93    EQU base+$D5D   ;len=41
OP94    EQU base+$115E  ;len=38
OP95    EQU base+$F5F   ;len=3
OP96    EQU base+$1060  ;len=15
OP97    EQU base+$961   ;len=37
OP98    EQU base+$1262  ;len=3
OP99    EQU base+$F63   ;len=3
OP100   EQU base+$864   ;len=14
OP101   EQU base+$1265  ;len=15
OP102   EQU base+$F66   ;len=17
OP103   EQU base+$667   ;len=3
OP104   EQU base+$A68   ;len=9
OP105   EQU base+$569   ;len=24
OP106   EQU base+$66A   ;len=8
OP107   EQU base+$136B  ;len=3
OP108   EQU base+$76C   ;len=42
OP109   EQU base+$36D   ;len=30
OP110   EQU base+$176E  ;len=32
OP111   EQU base+$136F  ;len=3
OP112   EQU base+$1070  ;len=24
OP113   EQU base+$C71   ;len=34
OP114   EQU base+$1372  ;len=29
OP115   EQU base+$B73   ;len=3
OP116   EQU base+$1274  ;len=19
OP117   EQU base+$A75   ;len=20
OP118   EQU base+$B76   ;len=22
OP119   EQU base+$1577  ;len=3
OP120   EQU base+$1478  ;len=8
OP121   EQU base+$F79   ;len=36
OP122   EQU base+$157A  ;len=17
OP123   EQU base+$E7B   ;len=3
OP124   EQU base+$87C   ;len=39
OP125   EQU base+$67D   ;len=36
OP126   EQU base+$E7E   ;len=38
OP127   EQU base+$47F   ;len=3
OP128   EQU base+$280   ;len=18
OP129   EQU base+$581   ;len=35
OP130   EQU base+$1682  ;len=3
OP131   EQU base+$1483  ;len=3
OP132   EQU base+$1184  ;len=15
OP133   EQU base+$1685  ;len=13
OP134   EQU base+$D86   ;len=18
OP135   EQU base+$1487  ;len=3
OP136   EQU base+$1288  ;len=5
OP137   EQU base+$1089  ;len=18
OP138   EQU base+$A8A   ;len=10
OP139   EQU base+$158B  ;len=3
OP140   EQU base+$B8C   ;len=30
OP141   EQU base+$128D  ;len=28
OP142   EQU base+$178E  ;len=33
OP143   EQU base+$158F  ;len=3
OP144   EQU base+$1390  ;len=20
OP145   EQU base+$991   ;len=32
OP146   EQU base+$1692  ;len=27
OP147   EQU base+$1593  ;len=3
OP148   EQU base+$1194  ;len=20
OP149   EQU base+$C95   ;len=18
OP150   EQU base+$1596  ;len=23
OP151   EQU base+$A97   ;len=3
OP152   EQU base+$D98   ;len=7
OP153   EQU base+$799   ;len=34
OP154   EQU base+$A9A   ;len=10
OP155   EQU base+$109B  ;len=3
OP156   EQU base+$49C   ;len=29
OP157   EQU base+$F9D   ;len=34
OP158   EQU base+$109E  ;len=38
OP159   EQU base+$D9F   ;len=3
OP160   EQU base+$3A0   ;len=14
OP161   EQU base+$6A1   ;len=31
OP162   EQU base+$DA2   ;len=17
OP163   EQU base+$8A3   ;len=3
OP164   EQU base+$13A4  ;len=17
OP165   EQU base+$EA5   ;len=15
OP166   EQU base+$AA6   ;len=20
OP167   EQU base+$CA7   ;len=3
OP168   EQU base+$11A8  ;len=7
OP169   EQU base+$12A9  ;len=12
OP170   EQU base+$CAA   ;len=10
OP171   EQU base+$BAB   ;len=3
OP172   EQU base+$5AC   ;len=32
OP173   EQU base+$16AD  ;len=30
OP174   EQU base+$15AE  ;len=35
OP175   EQU base+$17AF  ;len=3
OP176   EQU base+$11B0  ;len=20
OP177   EQU base+$BB1   ;len=34
OP178   EQU base+$17B2  ;len=31
OP179   EQU base+$DB3   ;len=3
OP180   EQU base+$EB4   ;len=22
OP181   EQU base+$13B5  ;len=20
OP182   EQU base+$12B6  ;len=25
OP183   EQU base+$DB7   ;len=3
OP184   EQU base+$CB8   ;len=8
OP185   EQU base+$9B9   ;len=36
OP186   EQU base+$DBA   ;len=12
OP187   EQU base+$ABB   ;len=3
OP188   EQU base+$7BC   ;len=38
OP189   EQU base+$4BD   ;len=36
OP190   EQU base+$ABE   ;len=41
OP191   EQU base+$FBF   ;len=3
OP192   EQU base+$CC0   ;len=15
OP193   EQU base+$6C1   ;len=33
OP194   EQU base+$FC2   ;len=3
OP195   EQU base+$3C3   ;len=16
OP196   EQU base+$11C4  ;len=18
OP197   EQU base+$10C5  ;len=14
OP198   EQU base+$FC6   ;len=16
OP199   EQU base+$DC7   ;len=3
OP200   EQU base+$14C8  ;len=5
OP201   EQU base+$13C9  ;len=11
OP202   EQU base+$ECA   ;len=5
OP203   EQU base+$16CB  ;len=3
OP204   EQU base+$DCC   ;len=33
OP205   EQU base+$14CD  ;len=29
OP206   EQU base+$16CE  ;len=31
OP207   EQU base+$12CF  ;len=3
OP208   EQU base+$ED0   ;len=20
OP209   EQU base+$17D1  ;len=33
OP210   EQU base+$15D2  ;len=31
OP211   EQU base+$12D3  ;len=3
OP212   EQU base+$13D4  ;len=3
OP213   EQU base+$10D5  ;len=19
OP214   EQU base+$12D6  ;len=21
OP215   EQU base+$13D7  ;len=3
OP216   EQU base+$11D8  ;len=18
OP217   EQU base+$FD9   ;len=35
OP218   EQU base+$13DA  ;len=11
OP219   EQU base+$CDB   ;len=6
OP220   EQU base+$BDC   ;len=3
OP221   EQU base+$9DD   ;len=35
OP222   EQU base+$8DE   ;len=37
OP223   EQU base+$BDF   ;len=3
OP224   EQU base+$5E0   ;len=15
OP225   EQU base+$CE1   ;len=37
OP226   EQU base+$BE2   ;len=3
OP227   EQU base+$7E3   ;len=3
OP228   EQU base+$EE4   ;len=18
OP229   EQU base+$13E5  ;len=15
OP230   EQU base+$BE6   ;len=16
OP231   EQU base+$AE7   ;len=3
OP232   EQU base+$10E8  ;len=5
OP233   EQU base+$7E9   ;len=26
OP234   EQU base+$14EA  ;len=6
OP235   EQU base+$12EB  ;len=3
OP236   EQU base+$AEC   ;len=33
OP237   EQU base+$DED   ;len=30
OP238   EQU base+$6EE   ;len=31
OP239   EQU base+$16EF  ;len=3
OP240   EQU base+$12F0  ;len=20
OP241   EQU base+$5F1   ;len=34
OP242   EQU base+$4F2   ;len=32
OP243   EQU base+$17F3  ;len=3
OP244   EQU base+$16F4  ;len=3
OP245   EQU base+$10F5  ;len=20
OP246   EQU base+$EF6   ;len=21
OP247   EQU base+$17F7  ;len=3
OP248   EQU base+$BF8   ;len=18
OP249   EQU base+$3F9   ;len=36
OP250   EQU base+$2FA   ;len=20
OP251   EQU base+$17FB  ;len=3
OP252   EQU base+$16FC  ;len=3
OP253   EQU base+$1FD   ;len=36
OP254   EQU base+$FE    ;len=37
OP255   EQU base+$15FF  ;len=3


;A in A'
;N,C and Z of flags in F'
;B,D and I flags in (vXflag)
;V in (vVflag)
;X in IYL
;Y in IYH
;S (stack) in E', D' will always contain SPrealpage as a constant
;PC in DE

;IX contains address of op_decode to do a fast jump to it (JP (IX) takes 8T)

        org PROGSTART
begin
        ld e,3+0x80 ;6912+keep
        ;ld e,0+0x80 ;EGA+keep
        OS_SETGFX ;e=0:EGA, e=2:MC, e=3:6912, e=6:text ;+SET FOCUS ;e=-1: disable gfx (out: e=old gfxmode)
        ;call setgfx

        ld e,0 ;color byte
        OS_CLS

        OS_GETMAINPAGES
;dehl=pages in 0000,4000,8000,c000
        ld a,e
        ;ld (pgmain4000),a
        ld a,h
        ;ld (pgmain8000),a
        ld a,l
        ;ld (pgspr),a  

        ld a,(user_scr0_high) ;ok
        SETPG4000

        ld hl,wasbasicrom
        ld de,0xe000
        ld bc,0x2000
        ldir
        ld hl,waskernalrom
        ld de,0x6000
        ld bc,0x2000
        ldir
        ld hl,wasloadfile
        ld de,loadfile
        ld bc,loadfile_sz
        ldir

        ld hl,COMMANDLINE ;command line
        call skipword
        call skipspaces
        ld a,(hl)
        or a
        jr z,noautoload
;command line = bk <file to load>"
       ld (filenameaddr),hl
       ;jr autoloadq
       
noautoload
;autoloadq
        jp GO
quit
        im 1
        ld hl,0
        QUIT

wasloadfile
        disp 0x5b00
loadfile
        im 1
        push af
        push bc
        push de
        push hl
        push ix
        push iy
        exx
        ex af,af' ;'
        push af
        push bc
        push de
        push hl
filenameaddr=$+1
        ld de,0
       
      ;ld hl,(0x7ffe)
      ;push hl
;de=filename
        OS_OPENHANDLE
        push bc
        ld de,loadfileaddr;0x7ffe
        ld hl,2;0x2002
        OS_READHANDLE
        pop bc
loadfileaddr=$+1
        ld de,0
       ld hl,0x2000
       or a
       sbc hl,de
        ld a,d
        and 0x1f
        or 0x80
        ld d,a
        ;ld hl,0x2000
        push bc
        OS_READHANDLE
        pop bc
        ld de,0x0200 ;next 8K
        ld hl,0x1e00
        push bc
        OS_READHANDLE
        pop bc
        OS_CLOSEHANDLE
      ;pop hl
      ;ld (0x7ffe),hl
        pop hl
        pop de
        pop bc
        pop af
        ex af,af' ;'
        exx
        pop iy
        pop ix
        pop hl
        pop de
        pop bc
        pop af
        im 2
        ret
        ent
loadfile_sz=$-wasloadfile

skipword
;hl=string
;out: hl=terminator/space addr
getword0
        ld a,(hl)
        or a
        ret z
        cp ' '
        ret z
        inc hl
        jr getword0

skipspaces
;hl=string
;out: hl=after last space
        ld a,(hl)
        cp ' '
        ret nz
        inc hl
        jr skipspaces

wasbasicrom
        incbin "vic20.rom"

        org 0x8000 ;will be 8k RAM
waskernalrom
        incbin "vic20a.rom"
        org 0xa000
        incbin "vic20f.rom"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        ORG base-$0200
GO
        DI              ;as we're messing with IX/IY disable interrupts!

                        ;6502 gets contents of
                        ;?FFFC and ?FFFD
                        ;and jumps to their contents
        LD SP,base-$20  ;for some reason TIMEX machines have silly values for SP!
        LD A,IM2page
        LD I,A
        IM 2            ;we want to trap interrupts so we can simulate 6502 IRQ

        LD A,$05
        LD BC,$1FFD
        NOP             ;these bytes can be changed to OUT (C),A
        NOP

superinit:
        LD BC,$FFFD     ;now port $FFFD
        LD A,$07
        OUT (C),A       ;select mixer control (R7)
        LD A,B          ;A=255 = all silent
        LD B,$BF        ;port BFFD now
        OUT (C),A       ;select tone only on channels A,B and C

reset:
        LD HL,(FFrealpage*256)+$FC
                        ;HL points to RESET vector on virtual machine
        LD E,(HL)
        INC HL
        LD D,(HL)       ;DE now contains correct PC value
        EXX
        LD DE,$0000
        PUSH DE         ;used to set AF' later
        LD D,SPrealpage
        DEC E           ;change E from 0 to FF, so SP starts at 01FF
        EXX
        LD HL,vXflag
        LD (HL),$34     ;on reset 6502 sets bit 5 (undef), 4 (brk), 2 (irq)
                        ;and resets bit 3 (dec). The rest are undefined
        DEC HL
        LD (HL),$00
        LD HL,VIC900F
        INC (HL)        ;force a change in background colour - forces graphics to redraw screen
               
        LD IY,$0000     ;X/Y to 0
        LD HL,trapback2here
        POP AF          ;set A and flags to 0
       
        PUSH HL         ;preload stack with right address
        DEC DE          ;dec PC by 1 to compensate for CLD increasing it
        EI              ;safe to enable interrupts now
        LD IX,rVIC900F  ;need to reset display after sorting out BCD mode
        JP cld          ;make sure ADC/SBC are correct
                        ;it will JP (IX) into rVIC900F which will sort the screen out
                        ;and will reset IX to op_decode

vVflag:
        DB $00
vXflag:
        DB $00

vtemp:
        DW $00


m1wrap:
;INC E has caused PC to wrap to next page
;therefore we have to re-translate DE

        INC D           ;next page
        LD L,D
        LD H,M1page
        LD H,(HL)       ;translated address in HL once LD L,E done
                        ;but that's done outside m1wrap routine
        RET


;increments PC by one
;used after most instructions
        macro incPC
        INC DE
        JP (IX)
        endm

        macro nextbyte
        INC DE
        LD L,D
        LD H,M1page
        LD H,(HL)
        LD L,E
        endm

;start with AF flags paged in
;end with them paged out
        macro opPHP
        LD HL,(vVflag)
        JR NZ,$+4
        SET 1,H
        JR NC,$+4
        SET 0,H
        JP P,$+5
        SET 7,H
        EX AF,AF' ;'
        LD A,H
        OR L
        endm
;L contains I,D,5 and B flags to be added
;notice that A is NOT PUSHED onto the stack!
;EX AF,AF' must also be done outside macro
;this MUST be done outside the macro
;(this is because BRK and IRQ have different requirements for B flag)

;pushes value in A on 6502 stack
;best to do this with AF paged out to preserve 6502/Z80 flags
        macro do_pushA
        EXX
        LD (DE),A
        DEC E
        EXX
        endm



; "illegal" opcodes come here

illegal3:
        INC DE          ;skip 3 bytes
illegal2:
        INC DE          ;skip 2 bytes
illegal:
        EX AF,AF' ;'
        LD A,$02
        OUT ($FE),A     ;make border red for now
        EX AF,AF' ;'
        incPC           ;skip just one byte


nmi_handler:
        LD HL,(M1page*256)+$26  ;LD H,$M1page instead of JR $-4
                                ;$26 is opcode for LD H,nn
        LD (op_decode+1),HL     ;reset 2 bytes to what they normally are

         call loadfile ;FIXME!

;PC (DE) is already correct at this point - so stack it

        EX AF,AF' ;'
        LD A,D
        do_pushA
        LD A,E
        do_pushA
        LD HL,(FFrealpage*256)+$FA      ;pre-validated page 255 value used
                        ;to get true NMI address
        LD E,(HL)
        INC L           ;safe to do as we know HL has to be FFFA - therfore validated page still valid
                        ;also L will never overflow so quicker to do INC L than INC HL
        LD D,(HL)       ;DE (PC) now contains  vector

        EX AF,AF' ;'
        opPHP           ;prepare flags in A to be stacked
;correct flags are in A
        AND $EF         ;force B bit clear (like IRQ)
        do_pushA        ;stack flags

        LD HL,vXflag
        LD A,(HL)
        AND $E3         ;clear bits 4,3 and 2
        OR $14          ;have to set bit 2 (I) 
        LD (HL),A
        EX AF,AF' ;'
        JP (IX)         ;no incPC as DE (PC) is already correct

signalNMI:
;currently an IRQ could override an NMI which is wrong!

        LD HL,$18+(irqND*256)
                                ;force interrupt to be serviced once
                                ;current 6502 instruction has finished
        LD (op_decode+1),HL     ;overwrite with JR irqN instruction
        RET                     ;eventually code will get to nmi_handler


;this is the main opcode decoding routine
;every 6502 instruction starts at op_decode.

irqN:   JP nmi_handler
irqM:   JP irq_handler

op_decode:
        LD L,D
        LD H,M1page     ;interrupts will overwrite this byte pair with a JR to irqM or irqN instruction
                        ;Remember - trapped I/O will alter IX, interrupts alter code.
                        ;(otherwise you can lose I/O or IRQ depending on order they happen)
                        ;This instruction is changed rather than first instruction as
                        ;2 bytes have to be changed and Z80 IRQ could occur in between the 2
                        ;instructions causing the Z80 to interpret the LD H,M1page as something
                        ;completely different

;remember DE contains the 6502 program counter
;we're now "translating" a 6502 page to a ZX Spectrum page
;ie: address $1000 (page $10) on the VIC translates into address $9000 (page $90) on the Spectrum
;see the M1 page table near the end of the code

        LD H,(HL)
        LD L,E          ;HL now contains translated address

;now HL contains a translated address we need to get the opcode at that address

        LD L,(HL)

;and work out address of routine to call

        LD H,Oppage
        LD H,(HL)

;each 6502 routine starts at an address where it's lsb = opcode value
;ie: RTS = opcode $60, so you'll find the RTS routine begins at $xx60

        JP (HL)         ;47T just to decode the opcode!
                        ;you can see the virtual 6502 is going to be a lot slower than the real thing!
                        ;adding INC DE and JP (IX) brings it up to 61T
                        ;therefore the shortest 6502 instruction (NOP) takes 61T to run
                        ;which gives 0.057 MIPS on a real Spectrum!
                        ;the real 6502 @1 MHz could do 0.5 MIPS - we are therefore nearly 9 times slower
                        ;in reality it is more like x15 times slower


irqND   EQU 256-(op_decode+3-irqN)      ;calculate JR displacements for self modifying-code
irqMD   EQU 256-(op_decode+3-irqM)

trapback2here:
;TRAPS will come back here
;traps are currently not used in this VIC 20 implmentation of 6502 core

        LD A,$04
        OUT ($FE),A
        DI
        HALT

;use this macro for BRAnch instructions (they don't have the INC DE bit)
        macro nextbyteB
        LD L,D
        LD H,M1page
        LD H,(HL)
        LD L,E
        endm

;use this macro when HL still contains validated address of DE
;#define nextbyte2              INC E
;#defcont               \       CALL Z,m1wrap
;#defcont               \       LD L,E

        macro nextbyte2
        INC DE
        LD L,D
        LD H,M1page
        LD H,(HL)
        LD L,E
        endm

;L(msb) and C(lsb) must be correct before using macro
        macro readtrap
        LD H,MRpage
        LD H,(HL)
        DEC H
        CALL Z,RAMread_trap
        LD L,C
        endm

;L(msb) and C(lsb) must be correct before using macro
        macro writetrap
        LD H,MWpage
        LD H,(HL)
        DEC H
        CALL Z,RAMwrite_trap
        LD L,C
        endm

;zero page
;a1=?PC
;d=?a1  (where a1 msb=00)
        macro getZP_read
        nextbyte
        LD L,(HL)
        LD H,ZPrealpage
        endm

;zero page,X
;a1=?PC
;d=?(a1+X)      (where a1 msb=00)
        macro getZPX_read
        nextbyte
        DB $FD
        LD A,L
        ADD A,(HL)
        LD L,A
        LD H,ZPrealpage
        endm

;zero page,Y
;a1=?PC
;d=?(a1+Y)      (where a1 msb=00)
        macro getZPY_read
        nextbyte
        DB $FD
        LD A,H
        ADD A,(HL)
        LD L,A
        LD H,ZPrealpage
        endm

;(indirect zero page)
;a1=?PC
;a2=?a1+256*?(a1+1)
;d=?a2
        macro get_ZP_read
        nextbyte
        LD L,(HL)
        LD H,ZPrealpage
        LD C,(HL)
        INC HL
        LD L,(HL)
;new address in HL (well it would be if LD H,(HL) was done instead along with LD L,C being added)
;but we need to validate this new address and that only needs msb
        readtrap
        endm

;absolute_read
;a1=?PC+256*?(PC+1)
;d=?a1
        macro getabsolute_read
        nextbyte
        LD C,(HL)
        nextbyte2
        LD L,(HL)
        readtrap
        endm

;absolute_RWM
;a1=?PC+256*?(PC+1)
;d=?a1
        macro getabsolute_RWM
        nextbyte
        LD C,(HL)
        nextbyte2
        LD B,(HL)
        PUSH BC
        LD L,B
        writetrap               //this sets IX
        POP BC
        LD L,B
        readtrap
        endm

;absolute_read,X
;a1=?PC+256*?(PC+1)
;a2=a1+X
;d=?a2
        macro getabsoluteX_read
        nextbyte
        LD A,(HL)
        DB $FD
        ADD A,L
        LD C,A
;correct lo-byte stored in C for use later
        nextbyte2
;BC would contains new absolute_read address, but we haven't set B yet - it's at (HL)
;now add X to it
;C,A and CFlag not affected by nextbyte(2)
        LD A,$00
        ADC A,(HL)
;would've been ADC A,B - but quicker to use ADC A,(HL) and ditch earlier LD B,(HL)
;need to add carry in case C+X overflowed from earlier
;thus the value in A is the correct hi-byte.
        LD L,A
;however the validation routine requires hi-byte in L
        readtrap
        endm

;absolute_read,Y
;a1=?PC+256*?(PC+1)
;a2=a1+Y
;d=?a2
        macro getabsoluteY_read
        nextbyte
        LD A,(HL)
        DB $FD
        ADD A,H
        LD C,A
;correct lo-byte stored in C for use later
        nextbyte2
        LD A,$00
        ADC A,(HL)
        LD L,A
        readtrap
        endm

;(indirect,X)
;a=(?PC)+X where msb=00
;a2=?a+256*?(a+1)
;d=?a2
        macro indirectX_read
        nextbyte
        DB $FD
        LD A,L
        ADD A,(HL)
        LD L,A
        LD H,ZPrealpage
;validated address in HL. We've used ADD A,L because address at this point is always page 0
        LD C,(HL)
        INC HL
;address is always validated because page 1 always follows page 0, and HL is 0-$FF
        LD L,(HL)
;validate new address (a2)
        readtrap
        endm

;(indirect),Y
;a=?PC
;a2=?a+256*?(a+1)
;a3=a2+Y
;d=?a3
        macro indirectY_read
        nextbyte
        LD L,(HL)
;we know H=0 at this point (zero page), so validate it using ZPrealpage constant
        LD H,ZPrealpage
        LD A,(HL)
        INC HL
;even if HL goes from FF to 00 (ie: into Page 1), the validated page 1 address is always +1 of validated page 0 address
;BC would contain new pointer - just got to add Y to it. B is still at (HL) and C is in A
        DB $FD
        ADD A,H
        LD C,A
;store lo-byte of result in C for later
        LD A,$00
        ADC A,(HL)
;would've been ADC A,B - but quicker to use ADC A,(HL) and ditch earlier LD B,(HL)
;need to add carry in case C+Y overflowed earlier
;this is hi-byte of result, but validation routine requires
;hi-byte in L so instead of saving in H we save in L
        LD L,A
        readtrap
        endm

;*** WRITE to memory versions***

;zero page
;a1=?PC
;d=?a1  (where a1 msb=00)
        macro getZP_write
        nextbyte
        LD L,(HL)
        LD H,ZPrealpage
        endm

;zero page,X
;a1=?PC
;d=?(a1+X)      (where a1 msb=00)
        macro getZPX_write
        nextbyte
        DB $FD
        LD A,L
        ADD A,(HL)
        LD L,A
        LD H,ZPrealpage
        endm

;zero page,Y
;a1=?PC
;d=?(a1+Y)      (where a1 msb=00)
        macro getZPY_write
        nextbyte
        DB $FD
        LD A,H
        ADD A,(HL)
        LD L,A
        LD H,ZPrealpage
        endm

;(indirect zero page)
;a1=?PC
;a2=?a1+256*?(a1+1)
;d=?a2
        macro get_ZP_write
        nextbyte
        LD L,(HL)
        LD H,ZPrealpage
        LD C,(HL)
        INC HL
        LD L,(HL)
;new address in HL (well it would be if LD H,(HL) was done instead along with LD L,C being added)
;but we need to validate this new address and that only needs msb
        writetrap
        endm

;absolute
;a1=?PC+256*?(a1+1)
;d=?a1
        macro getabsolute_write
        nextbyte
        LD C,(HL)
        nextbyte2
        LD L,(HL)
        writetrap
        endm

;absolute,X
;a1=?PC+256*?(a1+1)
;a2=a1+X
;d=?a2
        macro getabsoluteX_write
        nextbyte
        LD A,(HL)
        DB $FD
        ADD A,L
        LD C,A
;correct lo-byte stored in C for use later
        nextbyte2
;BC would contains new absolute_read address, but we haven't set B yet - it's at (HL)
;now add X to it
;C,A and CFlag not affected by nextbyte(2)
        LD A,$00
        ADC A,(HL)
;would've been ADC A,B - but quicker to use ADC A,(HL) and ditch earlier LD B,(HL)
;need to add carry in case C+X overflowed from earlier
;thus the value in A is the correct hi-byte.
        LD L,A
;however the validation routine requires hi-byte in L
        writetrap
        endm

;absolute,Y
;a1=?PC+256*?(a1+1)
;a2=a1+Y
;d=?a2
        macro getabsoluteY_write
        nextbyte
        LD A,(HL)
        DB $FD
        ADD A,H
        LD C,A
;correct lo-byte stored in C for use later
        nextbyte2
        LD A,$00
        ADC A,(HL)
        LD L,A
        writetrap
        endm

;(indirect,X)
;a=(?PC)+X where msb=00
;a2=?a+256*?(a+1)
;d=?a2
        macro indirectX_write
        nextbyte
        DB $FD
        LD A,L
        ADD A,(HL)
        LD L,A
        LD H,ZPrealpage
;validated address in HL. We've used ADD A,L because address at this point is always page 0
        LD C,(HL)
        INC HL
;address is always validated because page 1 always follows page 0, and HL is 0-$FF
        LD L,(HL)
;validate new address (d2)
        writetrap
        endm

;(indirect),Y
;a=?PC
;a2=?a+256*?(a+1)
;a3=a2+Y
;d=?a3
        macro indirectY_write
        nextbyte
        LD L,(HL)
;we know H=0 at this point (zero page), so validate it using ZPrealpage constant
        LD H,ZPrealpage
        LD A,(HL)
        INC HL
;even if HL goes from FF to 00 (ie: into Page 1), the validated page 1 address is always +1 of validated page 0 address
;BC would contain new pointer - just got to add Y to it. B is still at (HL) and C is in A
        DB $FD
        ADD A,H
        LD C,A
;store lo-byte of result in C for later
        LD A,$00
        ADC A,(HL)
;need to add carry in case C+Y overflowed earlier
;this is hi-byte of result, but validation routine requires
;hi-byte in L so instead of saving in H we save in L
        LD L,A
        writetrap
        endm



;the sequence INC A / DEC A gets the Z80 to set the S (6502 N flag)
;and Z flags correctly - without touching the C flag (which OR A would do)

;LDA - load accumulator
;set Z and N only
        macro opLDA
        LD A,(HL)
        INC A
        DEC A
        endm

;LDX - load X reg
;set Z and N only
        macro opLDX
        LD B,(HL)
        INC B
        DEC B
        DB $FD
        LD L,B
        endm

;LDY - load Y reg
;set Z and N only
        macro opLDY
        LD B,(HL)
        INC B
        DEC B
        DB $FD
        LD H,B
        endm

;DEC - decrease by one
;set Z and N only
        macro opDEC
        DEC (HL)
        endm
;flags set correctly by DEC instruction in this case


;INC - increase by one
;set Z and N only
        macro opINC
        INC (HL)
        endm
;flags set correctly by INC instruction in this case


;CPY - compare with Y
;set Z,N and C only
        macro opCPY
        LD B,A
        DB $FD
        LD A,H
        CP (HL)
        CCF
;flags set correctly by CP instruction in this case, except Carry flag
;which for some reason is the opposite on 6502 compared to Z80
        LD A,B
        endm


;CPX - compare with X
;set Z,N and C only
        macro opCPX
        LD B,A
        DB $FD
        LD A,L
        CP (HL)
        CCF
;flags set correctly by CP instruction in this case, except Carry flag
;which for some reason is the opposite on 6502 compared to Z80
        LD A,B
        endm


;CMP - compare with A
;set Z,N and C only
        macro opCMP
        CP (HL)
        CCF
        endm
;flags set correctly by CP instruction in this case, except Carry flag
;which for some reason is the opposite on 6502 compared to Z80

;STA - store A in memory
;no flags affected
        macro opSTA
        LD (HL),A
        endm

;STX - store X in memory
;no flags affected
        macro opSTX
        DB $FD
        LD B,L
        LD (HL),B
        endm

;STY - store Y in memory
;no flags affected
        macro opSTY
        DB $FD
        LD B,H
        LD (HL),B
        endm

;STZ - clear memory
;no flags affected
        macro opSTZ
        LD (HL),$00
        endm

;AND - bitwise AND against A
;set Z and N only
;Z80 clears C so have to be careful!
        macro opAND
        JR NC,$+7
        AND (HL)
        SCF
;put carry back to how it was
        incPC
        AND (HL)
;carry was already clear so doesn't matter about Z80 clearing it
;JP to here
        endm

;ORA - bitwise OR against A
;set Z and N only
;Z80 clears C so have to be careful!
        macro opORA
        JR NC,$+7
        OR (HL)
        SCF
;put carry back to how it was
        incPC
        OR (HL)
;carry was already clear so doesn't matter about Z80 clearing it
;JP to here
        endm

;EOR - bitwise EOR against A
;set Z and N only
;Z80 clears C so have to be careful!
        macro opEOR
        JR NC,$+7
        XOR (HL)
        SCF
;put carry back to how it was
        incPC
        XOR (HL)
;carry was already clear so doesn't matter about Z80 clearing it
;JP to here
        endm

;ADC - add with carry to A
;sets C,Z,V and N flags
        macro opADC
        JP sharedadc2
        endm


;TRB - test and reset bits
;sets Z flag only
        macro opTRB
        PUSH AF
        CPL
        AND (HL)
        LD (HL),A
        JR NZ,$+$06
;result was 0, so we need to set Z flag (and Z flag only!)
        POP HL
;flags in L
        SET 6,L
;set Z flag (bit 6 on Z80)
        PUSH HL
;back here from earlier JR
        POP AF
        endm

;TSB - test and set bits
;sets Z flag only
        macro opTSB
        PUSH AF
        OR (HL)
        LD (HL),A
        JR NZ,$+$06
;result was 0, so we need to set Z flag (and Z flag only!)
        POP HL
;flags in L
        SET 6,L
;set Z flag (bit 6 on Z80)
        PUSH HL
;back here from earlier JR
        POP AF
        endm

;ASL - arithmetic shift left
;sets C,Z and N flags
        macro opASL
        SLA (HL)
        endm

;LSR - logical shift right
;sets C,Z and N flags
        macro opLSR
        SRL (HL)
        endm

;ROL - rotate left one bit
;sets C,Z and N flags
        macro opROL
        RL (HL)
        endm

;ROR - rotate right one bit
;sets C,Z and N flags
        macro opROR
        RR (HL)
        endm

;SBC - subtract with carry from accumulator
;sets C,Z,V and N flags
        macro opSBC
        JP sharedsbc2
        endm

;BIT - test bits against accumulator (AND without storing result)
;sets Z,V and N flags
;except BIT #imm which only sets Z (CMOS)
        macro opBIT
        LD B,A
        LD C,(HL)
        INC C
        DEC C
        PUSH AF
        LD A,C
        AND $40
        LD (vVflag),A
        LD A,B
        AND C
        JR NZ,$+6
        POP HL
        SET 6,L
        PUSH HL
        POP AF
        endm


WriteScreen:
        LD IX,op_decode
RAMtrap EQU $+1
        LD HL,RAMtrap   ;this value gets overwritten as required
WriteDirect:
        EX AF,AF' ;'    ;save 6502 AF
        LD C,(HL)       ;get contents of that byte - to be used later
        LD A,H
        PUSH DE         ;preserve DE (PC)
                        ;on VIC it's 23 lines of 22 chars!
        SUB ScreenPage  ;make address become an offset of 0000-01FF
        LD H,A          ;HL contains offset
WriteScreenSize:
;the following parameters can be adjusted by POKEing the VIC
        LD DE,$FFEA     ;-22 chars per row!
        LD B,$17        ;23 rows
                        ;if B=0 here then trouble arises...
write0:
        ADD HL,DE
        JR NC,write1
        DJNZ write0

        JP display7bytes8       ;outside range of ZX screen so abort
                                ;or some clever person set col size to 0!
                                ;have to jump to this addr and not "write3"
                                ;as we don't know if we're in 8 or 16 row mode
write1:
;at this point B sort of contains number of rows
;and the remainder in HL (should be <255 so use just L) is number of columns

        LD A,$17
        SUB B           ;row count in A

        SBC HL,DE       ;correct for taking off 1 too many rows
                        ;CF cleared by SUB B above

        ADD A,A         ;double up so if A was 3, it's now 6
        LD E,A
        LD D,rowtable/256       ;rowtable msb - DE points to correct entry in table
        EX DE,HL        ;now HL points there
        LD (RAMtrap),HL ;store for later
        LD A,(HL)
        INC L
        LD D,(HL)
        ADD A,E         ;add in column from HL (now DE)
        LD E,A          ;DE now contains correct addr on screen


writescreen3:
        LD L,C          ;C contains (HL) from earlier
        LD H,$00        ;copy into HL

writescreen4:
        ADD HL,HL       ;contains NOP for 8 high characters
                        ;or ADD HL,HL for 16 high characters
        ADD HL,HL       ;multiply by 2
        ADD HL,HL       ;multiply by 4
        ADD HL,HL       ;multiply by 8
WriteScreenCharset:
        LD BC,chargen
        ADD HL,BC       ;reference into character map
write2:
        LD A,(HL)
        LD (DE),A
        INC D
        INC L           ;safe to use INC L rather than INC HL as charmap always on 1024 byte boundary
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A       ;don't need last 2 INC D/L - no point!

write3:
write3a:
;the following 7 (8!!!) bytes change depending on 8 or 16 row display mode
        LD A,E
        AND $1F         ;keep column
        LD B,A          ;in B for safe keeping
        LD DE,(RAMtrap)

        INC E
        INC E           ;INC DE twice to get to next row (2nd half of 16-bytes)
        LD A,(DE)
        LD C,A
        INC E
        LD A,(DE)
        LD D,A
        LD A,B          ;column
        ADD A,C         ;add to DE
        LD E,A          ;new row addr in DE
                        ;HL continues where we left off
write16:
        LD A,(HL)
        LD (DE),A
        INC D
        INC L           ;safe to use INC L rather than INC HL as charmap always on 1024 byte boundary
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A
        INC D
        INC L
        LD A,(HL)
        LD (DE),A       ;don't need last 2 INC D/L - no point!
       
display7bytes8:
        POP DE          ;restore PC
        EX AF,AF' ;'    ;back to using real AF
        JP (IX)

display7bytes16:
        LD A,E
        AND $1F         ;keep column
        LD B,A          ;in B for safe keeping
        LD DE,(RAMtrap) ;yes I know that makes 8 bytes!!

        EX AF,AF' ;'    ;back to using real AF
        JP (IX)

attr2:
        EX AF,AF' ;'    ;preserve 6502 AF
        LD IX,op_decode
        PUSH DE         ;preserve DE
        LD HL,(RAMtrap) ;get back what we wrote to earlier

;ATTR3-$0B IS HERE
attr30B:
        PUSH HL         ;preserve it for later
        LD A,H
        AND $01         ;this clears CF flag
        LD H,A          ;HL now contains 0-1FF = offset from top left
        LD DE,(WriteScreenSize+1)       ;width in DE
        LD B,$17        ;max 24 rows - value changed as necessary
attr3:
        ADD HL,DE
        JR NC,attr4
        DJNZ attr3
        POP HL          ;drop HL off stack
        JR attrEnd      ;outside screen area so abort!
attr4:
        SBC HL,DE       ;compensate for going too far
attr4a:
        LD A,$17        ;this value is changed as necessary
        SUB B           ;row counter in A
                        ;remainder (column) in L
attr316:
        NOP             ;this is NOP for 8-row high chars or
                        ;ADD A,A for 16-row high chars!

        LD C,L          ;copy col to C for later
        ADD A,A         ;safe to double up in 8 bits
                        ;as row should be <32
        ADD A,A         ;x4
        ADD A,A         ;x8
        LD L,A
        LD H,$16        ;now go to 16 bit addition
                        ;set H to $0B, so when x8 it goes to $58
                        ;set H to $16, so when x4 it goes to $58
                        ;which is $5800 for attribute area
        ADD HL,HL       ;x16
        ADD HL,HL       ;x32 - 32 bytes per row
        LD A,L
        ADD A,C         ;include columns
        LD L,A

        POP DE          ;get earlier RAMtrap addr back as DE
        LD A,(DE)
        AND $07         ;keep only colour nybble - ignore multicolour
        LD B,A          ;INK into B

        LD A,(VIC900F)  ;get background colour
                        ;are we inversed?

attr4b: ;patch jr nc/c
        JR NC,attr5     ;C is always RESET by AND entry above

attr6:
;no inverse
        AND $F0         ;by keeping only bits 7-4
        OR B            ;merge in INK
        JP attr7

attr5:
;inverse
        AND $F0
        OR B
        RRCA
        RRCA
        RRCA
        RRCA            ;swap INK and PAPER nybbles

attr7:
;A now contains 0-255 value which we reference into table to convert into Spectrum ATTR value

        LD E,A
        LD D,attrs/256
        LD A,(DE)       ;convert VIC value (bbbb0fff) into ZX ATTR value
        LD (HL),A       ;update Spectrum screen

attrEnd:
        POP DE          ;restore DE
       
        EX AF,AF' ;'    ;restore 6502 AF
        JP (IX)


        ORG base-$20
RELOCATE_KERNAL:
        LD HL,$8000
        LD DE,$6000
        LD BC,$2000
        LDIR
        JP base-$0200

        ORG base-$08
ANTIPRG:
        LD HL,$FF72
        LD ($7FFE),HL
        JP (IX)

;start of routines

        ORG base

brk:
        INC DE          ;skip past BRK instruction
        INC DE          ;remember BRK increases PC by 1 to skip following byte
                        ;before stacking PC - unlike JSR
        EX AF,AF'
        LD A,D
        do_pushA
        LD A,E
        do_pushA
        LD HL,FFrealpage*256+$FE        ;validate it!
        LD E,(HL)
        INC L           ;safe to do as we know HL has to be FFFE - therfore validated page still valid
                        ;also L will never overflow so quicker to do INC L than INC HL
        LD D,(HL)       ;DE (PC) now contains  vector

        EX AF,AF'

        opPHP           ;prepare flags in A
        OR $10          ;force B flag to be set (only IRQ pushes this bit as 0)

;correct flags are in A
        do_pushA

        LD HL,vXflag
        LD A,(HL)
        AND $E3         ;clear bits 4,3 and 2
        OR $14          ;have to set bits 4 (B) and 2 (I)      
        LD (HL),A
        EX AF,AF'
        JP (IX)         ;no incPC as DE (PC) is already correct

        ORG OP1
ora_zpx:
        EX AF,AF'

        indirectX_read
        EX AF,AF'
        opORA
        incPC

        ORG OP2
        JP illegal2

        ORG OP3
        JP illegal

        ORG OP4
tsbzp:
        getZP_read
        opTSB
        incPC

        ORG OP5
orazp:
        getZP_read
        opORA
        incPC

        ORG OP6
aslzp:
        getZP_read
        opASL
        incPC

        ORG OP7
        JP illegal

        ORG OP8
php:
;start with flags in...
        opPHP
;flags now out
        OR $10          ;force B flag to be set (only IRQ pushes this bit as 0)
        do_pushA
        EX AF,AF'

        incPC

        ORG OP9
ora_imm:
        nextbyte
        opORA
        incPC

        ORG OP10
;ASL - arithmetic shift left
;sets C,Z and N flags
asla:   SLA A
        incPC

        ORG OP11
        JP illegal

        ORG OP12
tsbA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opTSB
        incPC

        ORG OP13
oraA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opORA
        incPC

        ORG OP14
aslA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opASL
        incPC

        ORG OP15
        JP illegal

        ORG OP16
bpl:
        INC DE
        JP M,bpl2
        EX AF,AF'
        nextbyteB
        LD L,(HL)
        LD A,L
        RLCA
        SBC A,A
        LD H,A
        ADD HL,DE       ;add displacement to PC (DE)
        EX DE,HL
        EX AF,AF'

bpl2:
        incPC

        ORG OP17
ora_zp_y:
        EX AF,AF'
        indirectY_read
        EX AF,AF'

        opORA
        incPC

        ORG OP18
ora_zp:
        get_ZP_read
        opORA
        incPC

        ORG OP19
        JP illegal

        ORG OP20
trbzp:
        getZP_read
        opTRB
        incPC

        ORG OP21
orazpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opORA
        incPC

        ORG OP22
aslzpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opASL
        incPC

        ORG OP23
        JP illegal

        ORG OP24
clc:
        SCF
        CCF             ;no clear carry flag on Z80, so have to set then compliment it!
        incPC

        ORG OP25
oraay:
        EX AF,AF'
        getabsoluteY_read
        EX AF,AF'

        opORA
        incPC

        ORG OP26
;INC - increase by one
;set Z and N only
inca:   INC A
;flags set correctly by INC instruction in this case
        incPC

        ORG OP27
        JP illegal

        ORG OP28
trbA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opTRB
        incPC

        ORG OP29
oraax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opORA
        incPC

        ORG OP30
aslax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opASL
        incPC

        ORG OP31
        JP illegal

        ORG OP32
jsrA:
        EX AF,AF'
        nextbyte
        LD C,(HL)
        nextbyte2
        LD A,D
        do_pushA        ;we'
re pushing PC+2 here rather than PC+3 (ie: byte after JSR b1b2)
        LD A,E          ;as RTS does +1 to POP'd result
        do_pushA
        LD E,C
        LD D,(HL)       ;quicker to do this than LD B,(HL) , LD D,B
        EX AF,AF'
        JP (IX)         ;go straight to decode routine

        ORG OP33
and_zpx:
        EX AF,AF'

        indirectX_read
        EX AF,AF'
        opAND
        incPC

        ORG OP34
        JP illegal2

        ORG OP35
        JP illegal

        ORG OP36
bitzp:
        getZP_read
        opBIT
        incPC

        ORG OP37
andzp:
        getZP_read
        opAND
        incPC

        ORG OP38
rolzp:
        getZP_read
        opROL
        incPC

        ORG OP39
        JP illegal

        ORG OP40
plp:
        LD B,A          ;keep A
        EXX
        INC E           ;SP is 0100-01FF only
        LD A,(DE)
        EXX
plp2:
        LD C,A          ;take copy of flags!
        AND $40
        LD L,A

        LD A,C
        AND $3C
        OR $20
        LD H,A
        LD (vVflag),HL  ;53T

        LD A,C          ;restore flags
        AND $83         ;keep N+C+Z flags (6502)
        ADD A,$3E       ;move Z flag from bit 1 to bit 6 if it was set
        LD C,A          ;new flags in L
        PUSH BC         ;(B has real 6502 Acc from earlier)
        LD A,(adcdaa2)
        XOR H
        AND $08         ;=0 at this point means we don'
t need to change BCD mode
        JR NZ,plp32

        POP AF          ;reset C,Z,N flags
        incPC
plp32:
        BIT 3,H
        JR NZ,plp42
        POP AF
        JP cld2
plp42:
        POP AF          ;reset C,Z,N flags
        JP sed2         ;jump into SED routine

        ORG OP41
and_imm:
        nextbyte
        opAND
        incPC

        ORG OP42
;ROL - rotate left one bit
;sets C,Z and N flags
rola:   ADC A,A         ;can't use RLA (4T) as it doesn't set Z and N flags
                        ;ADC A,A does same as "RL A" (except H and V flags), but is 4T quicker
        incPC

        ORG OP43
        JP illegal

        ORG OP44
bitA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opBIT
        incPC

        ORG OP45
andA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opAND
        incPC

        ORG OP46
rolA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opROL
        incPC

        ORG OP47
        JP illegal


        ORG OP48
bmi:
        INC DE
        JP P,bmi2
        EX AF,AF'
        nextbyteB
        LD L,(HL)
        LD A,L
        RLCA
        SBC A,A
        LD H,A
        ADD HL,DE       ;add displacement to PC (DE)
        EX DE,HL
        EX AF,AF'

bmi2:
        incPC

        ORG OP49
and_zp_y:
        EX AF,AF'
        indirectY_read
        EX AF,AF'

        opAND
        incPC

        ORG OP50
and_zp:
        get_ZP_read
        opAND
        incPC

        ORG OP51
        JP illegal

        ORG OP52
bitzpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opBIT
        incPC

        ORG OP53
andzpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opAND
        incPC

        ORG OP54
rolzpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opROL
        incPC

        ORG OP55
        JP illegal

        ORG OP56
sec:
        SCF
        incPC

        ORG OP57
anday:
        EX AF,AF'
        getabsoluteY_read
        EX AF,AF'

        opAND
        incPC

        ORG OP58
;DEC - decrease by one
;set Z and N only
deca:   DEC A
;flags set correctly by DEC instruction in this case
        incPC

        ORG OP59
        JP illegal

        ORG OP60
bitax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opBIT
        incPC

        ORG OP61
andax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opAND
        incPC

        ORG OP62
rolax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opROL
        incPC

        ORG OP63
        JP illegal

        ORG OP64
rti:
;pops address and status register
;doesn't +1 to PC!
;now doesn't destroy H'L'
        LD B,A          ;keep 6502 Acc for PLP2
        EXX
        INC E
        LD A,(DE)       ;status byte off stack first
        INC E
        PUSH DE
        INC E           ;another 2 bytes off stack
        EXX
        POP HL
        LD E,(HL)       ;lo byte next
        INC L
        LD D,(HL)       ;hi byte last
        DEC DE          ;adjust PC so the incPC at end of PLP
                        ;doesn't affect us
        JP plp2         ;carry on inside PLP (we've still got wrong AF)


        ORG OP65
eor_zpx:
        EX AF,AF'
        indirectX_read
        EX AF,AF'

        opEOR
        incPC

        ORG OP66
        JP illegal2

        ORG OP67
        JP illegal

        ORG OP68
        JP illegal2

        ORG OP69
eorzp:
        getZP_read
        opEOR
        incPC

        ORG OP70
lsrzp:
        getZP_read
        opLSR
        incPC
       
        ORG OP71
        JP illegal

        ORG OP72
;PHA - push A
;no flags affected
pha:
        EXX
        LD (DE),A
        EX AF,AF'
        DEC E
        EX AF,AF'

        EXX
        incPC

        ORG OP73
eor_imm:
        nextbyte
        opEOR
        incPC

        ORG OP74
;LSR - logical shift right
;sets C,Z and N flags
lsra:   SRL A
        incPC

        ORG OP75
        JP illegal

        ORG OP76
jmpA:
        nextbyte
        LD C,(HL)
        nextbyte2
        LD D,(HL)
        LD E,C
        JP (IX)

        ORG OP77
eorA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opEOR
        incPC
       
        ORG OP78
lsrA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opLSR
        incPC

        ORG OP79
        JP illegal

        ORG OP80
bvc:
        EX AF,AF'
        INC DE
        LD A,(vVflag)
        AND A           ;is it zero?
        JR NZ,bvc2
        nextbyteB
        LD L,(HL)
        LD A,L
        RLCA
        SBC A,A
        LD H,A
        ADD HL,DE       ;add displacement to PC (DE)
        EX DE,HL
bvc2:
        EX AF,AF'

        incPC

        ORG OP81
eor_zp_y:
        EX AF,AF'
        indirectY_read
        EX AF,AF'

        opEOR
        incPC

        ORG OP82
eor_zp:
        get_ZP_read
        opEOR
        incPC

        ORG OP83
        JP illegal

        ORG OP84
        JP illegal2

        ORG OP85
eorzpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opEOR
        incPC

        ORG OP86
lsrzpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opLSR
        incPC

        ORG OP87
        JP illegal

        ORG OP88
cli:
        LD HL,vXflag
        RES 2,(HL)      ;reset I bit
        incPC

        ORG OP89
eoray:
        EX AF,AF'
        getabsoluteY_read
        EX AF,AF'

        opEOR
        incPC

        ORG OP90
;PHY - push Y
;no flags affected
phy:
        EXX
        EX AF,AF'

        DB $FD
        LD A,H

        LD (DE),A
        DEC E
        EX AF,AF'

        EXX
        incPC

        ORG OP91
        JP illegal

        ORG OP92
        JP illegal3

        ORG OP93
eorax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opEOR
        incPC

        ORG OP94
lsrax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opLSR
        incPC

        ORG OP95
        JP illegal

        ORG OP96
rts:
;pops address and adds 1!
        EX AF,AF'
        EXX
        INC E
        LD A,(DE)       ;lo-byte off stack first
        EXX
        LD E,A          ;set lo-byte of PC
        EXX
        INC E
        LD A,(DE)       ;hi-byte off stack
        EXX
        LD D,A          ;DE now contains value off stack
        EX AF,AF'

        incPC           ;incPC does the +1 to PC for us

        ORG OP97
adc_zpx:
        EX AF,AF'
        indirectX_read
        EX AF,AF'

        opADC
        incPC

        ORG OP98
        JP illegal2

        ORG OP99
        JP illegal

        ORG OP100
stzzp:
        getZP_write
        opSTZ
        incPC

        ORG OP101
adczp:
        getZP_read
        opADC
        incPC

        ORG OP102
rorzp:
        getZP_read
        opROR
        incPC

        ORG OP103
        JP illegal

        ORG OP104
;PLA - pull A
;sets Z and N flags
pla:
        EXX
        INC E           ;this will upset flags - but we're going to change them anyway!
        LD A,(DE)
        INC A
        DEC A
        EXX
        incPC

        ORG OP105
adc_imm:
        nextbyte

;every other ADC instruction has to JP to here
;but ADC #imm carries straight on into code

sharedadc2:
;all ADC opcodes come here - it's quicker to waste 10T
;on a JP instruction then to test D flag and JP according to it

        ADC A,(HL)
;the following byte is modified by SED and CLD instructions
;to either NOP (CLD) or DAA (SED)

adcdaa2:
        DAA

;C,Z and N are correct
;but V needs to be stored elsewhere

        LD HL,vVflag
        JP PO,$+8
;jump if overflow unset
;A=$40 if V set, A=0 if V reset
        LD (HL),$40
        incPC
;no overflow comes here
        LD (HL),$00
        incPC

        ORG OP106
;ROR - rotate right one bit
;sets C,Z and N flags
rora:   RR A            ;can't use RRA (4T) as it doesn't set Z and N flags
        incPC

        ORG OP107
        JP illegal

        ORG OP108
jmp_a:
        EX AF,AF'
        nextbyte
        LD C,(HL)
        nextbyte2
        LD D,(HL)
        LD E,C          ;address following JMP into DE
        LD L,D          ;pre-load L for readtrap (C already OK)
        readtrap
        LD B,(HL)
        INC DE
        LD C,E
        LD L,D          ;pre-load L and C for readtrap
        readtrap
        LD D,(HL)
        LD E,B          ;get (DE) into PC
        EX AF,AF'

        JP (IX)

        ORG OP109
adcA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opADC
        incPC

        ORG OP110
rorA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opROR
        incPC

        ORG OP111
        JP illegal

        ORG OP112
bvs:
        EX AF,AF'
        INC DE
        LD A,(vVflag)
        AND A           ;is it zero?
        JR Z,bvs2
        nextbyteB
        LD L,(HL)
        LD A,L
        RLCA
        SBC A,A
        LD H,A
        ADD HL,DE       ;add displacement to PC (DE)
        EX DE,HL
bvs2:
        EX AF,AF'

        incPC

        ORG OP113
adc_zp_y:
        EX AF,AF'
        indirectY_read
        EX AF,AF'

        opADC
        incPC

        ORG OP114
adc_zp:
        get_ZP_read
        opADC
        incPC

        ORG OP115
        JP illegal

        ORG OP116
stzzpx:
        EX AF,AF'
        getZPX_write
        EX AF,AF'

        opSTZ
        incPC
       
        ORG OP117
adczpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opADC
        incPC

        ORG OP118
rorzpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opROR
        incPC

        ORG OP119
        JP illegal

        ORG OP120
sei:
        LD HL,vXflag
        SET 2,(HL)      ;set I bit
        incPC

        ORG OP121
adcay:
        EX AF,AF'
        getabsoluteY_read
        EX AF,AF'

        opADC
        incPC

        ORG OP122
;PLY - pull Y
;sets Z and N flags
ply:
        EXX
        EX AF,AF'
        INC E
        LD A,(DE)
        EXX
        LD B,A
        EX AF,AF'

        INC B
        DEC B
        DB $FD
        LD H,B
        incPC

        ORG OP123
        JP illegal

        ORG OP124
jmp_ax:
;this is a one off instruction, but it's similar to absoluteX_read...
        EX AF,AF'
        nextbyte
        LD C,(HL)
        nextbyte2
        LD B,(HL)
        DB $FD
        LD A,L
        LD L,A
        LD H,$00
        ADD HL,BC       ;now got address + X in HL
        EX DE,HL        ;set DE to this address
        nextbyteB       ;don'
t increase DE as it's already correct!
        LD C,(HL)
        nextbyte2
        LD D,(HL)
        LD E,C          ;DE now contains new addr for PC
        EX AF,AF'

        JP (IX)

        ORG OP125
adcax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opADC
        incPC

        ORG OP126
rorax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opROR
        incPC

        ORG OP127
        JP illegal

        ORG OP128
bra:    EX AF,AF'
        nextbyte
        LD L,(HL)       ;displacement
        LD A,L          ;copy into A
        RLCA            ;bit 7 into C flag
        SBC A,A         ;if C=0 then A=0, C=1 then A=FF
        LD H,A          ;which is correct value for H in HL
        ADD HL,DE       ;add displacement to PC (DE)
        EX DE,HL
        EX AF,AF'

        incPC

        ORG OP129
sta_zpx:
        EX AF,AF'
        indirectX_write
        EX AF,AF'

        opSTA
        incPC

        ORG OP130
        JP illegal2

        ORG OP131
        JP illegal

        ORG OP132
styzp:
        getZP_write
        opSTY
        incPC

        ORG OP133
stazp:
        getZP_write
        opSTA
        incPC

        ORG OP134
stxzp:
        getZP_write
        opSTX
        incPC

        ORG OP135
        JP illegal

        ORG OP136
dey:    DB $FD
        DEC H
;flags set correctly by DEC instruction in this case
        incPC

        ORG OP137
bit_imm:
        PUSH AF         ;get flags and 6502 A
        nextbyte
;BIT #imm does not set V and N flags, so we can't use opBIT
        AND (HL)        ;do the BIT test against the immediate value
        JR NZ,$+6       ;if A<>0 then skip setting real Z flag
        POP BC
        SET 6,C         ;Z80 Z flag is bit 6
        PUSH BC
;earlier JR comes in here
        POP AF          ;restores 6502 A and sets flags
        incPC


        ORG OP138
;TXA - transfer X to A
;sets Z and N flags
txa:
        DB $FD
        LD A,L
        INC A
        DEC A
        incPC

        ORG OP139
        JP illegal

        ORG OP140
styA:
        EX AF,AF'
        getabsolute_write
        EX AF,AF'

        opSTY
        incPC

        ORG OP141
staA:
        EX AF,AF'
        getabsolute_write
        EX AF,AF'

        opSTA
        incPC

        ORG OP142
stxA:
        EX AF,AF'
        getabsolute_write
        EX AF,AF'

        opSTX
        incPC

        ORG OP143
        JP illegal

        ORG OP144
bcc:
        INC DE
        JR C,bcc2
        EX AF,AF'
        nextbyteB
        LD L,(HL)
        LD A,L
        RLCA
        SBC A,A
        LD H,A
        ADD HL,DE       ;add displacement to PC (DE)
        EX DE,HL
        EX AF,AF'

bcc2:
        incPC

        ORG OP145
sta_zp_y:
        EX AF,AF'
        indirectY_write
        EX AF,AF'

        opSTA
        incPC

        ORG OP146
sta_zp:
        get_ZP_write
        opSTA
        incPC

        ORG OP147
        JP illegal

        ORG OP148
styzpx:
        EX AF,AF'
        getZPX_write
        EX AF,AF'

        opSTY
        incPC

        ORG OP149
stazpx:
        EX AF,AF'
        getZPX_write
        EX AF,AF'

        opSTA
        incPC

        ORG OP150
stxzpy:
        EX AF,AF'
        getZPY_write
        EX AF,AF'

        opSTX
        incPC

        ORG OP151
        JP illegal

        ORG OP152
;TYA - transfer Y to A
;sets Z and N flags
tya:
        DB $FD
        LD A,H
        INC A
        DEC A
        incPC

        ORG OP153
staay:
        EX AF,AF'
        getabsoluteY_write
        EX AF,AF'

        opSTA
        incPC

        ORG OP154
;TXS - transfer SP to X
;doesn't set any flags (unlike TSX)
txs:
        EXX
        DB $FD
        LD E,L
        EXX
        incPC

        ORG OP155
        JP illegal

        ORG OP156
stzA:
        EX AF,AF'
        getabsolute_write
        EX AF,AF'

        opSTZ
        incPC

        ORG OP157
staax:
        EX AF,AF'
        getabsoluteX_write
        EX AF,AF'

        opSTA
        incPC

        ORG OP158
stzax:
        EX AF,AF'
        getabsoluteX_write
        EX AF,AF'

        opSTZ
        incPC

        ORG OP159
        JP illegal

        ORG OP160
ldy_imm:
        nextbyte
        opLDY
        incPC

        ORG OP161
lda_zpx:
        EX AF,AF'
        indirectX_read
        EX AF,AF'

        opLDA
        incPC

        ORG OP162
ldx_imm:
        nextbyte
        opLDX
        incPC
       
        ORG OP163
        JP illegal

        ORG OP164
ldyzp:
        getZP_read
        opLDY
        incPC

        ORG OP165
ldazp:
        getZP_read
        opLDA
        incPC

        ORG OP166
ldxzp:
        getZP_read
        opLDX
        incPC

        ORG OP167
        JP illegal

        ORG OP168
;TAY - transfer A to Y
;sets Z and N flags
tay:
        INC A
        DEC A
        DB $FD
        LD H,A
        incPC

        ORG OP169
;op173 starts very soon after op169!
lda_imm:
        nextbyte
        opLDA
        incPC

        ORG OP170
;TAX - transfer A to X
;sets Z and N flags
tax:
        INC A
        DEC A
        DB $FD
        LD L,A
        incPC

        ORG OP171
        JP illegal

        ORG OP172
ldyA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opLDY
        incPC

        ORG OP173
ldaA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opLDA
        incPC

        ORG OP174
ldxA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opLDX
        incPC

        ORG OP175
        JP illegal

        ORG OP176
bcs:
        INC DE
        JR NC,bcs2
        EX AF,AF'
        nextbyteB
        LD L,(HL)       ;better to do this after checking flag - not before!
        LD A,L
        RLCA
        SBC A,A
        LD H,A
        ADD HL,DE       ;add displacement to PC (DE)
        EX DE,HL
        EX AF,AF'

bcs2:
        incPC


        ORG OP177
lda_zp_y:
        EX AF,AF'
        indirectY_read
        EX AF,AF'

        opLDA
        incPC

        ORG OP178
lda_zp:
        EX AF,AF'
        get_ZP_read
        EX AF,AF'

        opLDA
        incPC

        ORG OP179
        JP illegal

        ORG OP180
ldyzpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opLDY
        incPC

        ORG OP181
ldazpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opLDA
        incPC

        ORG OP182
ldxzpy:
        EX AF,AF'
        getZPY_read
        EX AF,AF'

        opLDX
        incPC

        ORG OP183
        JP illegal

        ORG OP184
clv:
        LD HL,vVflag
        LD (HL),$00
        incPC

        ORG OP185
ldaay:
        EX AF,AF'
        getabsoluteY_read
        EX AF,AF'

        opLDA
        incPC

        ORG OP186
;TSX - transfer SP to X
;sets Z and N flags
tsx:
        EXX
        DB $FD
        LD L,E
        INC E           ;quicker to INC E then to transfer A into A' and INC that
        DEC E
        EXX
        incPC

        ORG OP187
        JP illegal

        ORG OP188
ldyax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opLDY
        incPC

        ORG OP189
ldaax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opLDA
        incPC

        ORG OP190
ldxay:
        EX AF,AF'
        getabsoluteY_read
        EX AF,AF'

        opLDX
        incPC

        ORG OP191
        JP illegal

        ORG OP192
cpy_imm:
        nextbyte
        opCPY
        incPC

        ORG OP193
cmp_zpx:
        EX AF,AF'
        indirectX_read
        EX AF,AF'

        opCMP
        incPC

        ORG OP194
        JP illegal2

        ORG OP195
        JP illegal
;or use alternative opcode "ostrap"
;it allows seamless merging of 6502 and Z80 code
;use as $C3 lsb msb to call your Z80 code from 6502
;and finish Z80 code with call to 'rts' routine
;
;ostrap:
;       nextbyte
;       LD C,(HL)
;       nextbyte2
;       LD H,(HL)
;       LD L,C
;       JP (HL)         ;yikes!!

        ORG OP196
cpyzp:
        getZP_read
        opCPY
        incPC

        ORG OP197
cmpzp:
        getZP_read
        opCMP
        incPC

        ORG OP198
deczp:
        getZP_read
        opDEC
        incPC

        ORG OP199
        JP illegal

        ORG OP200
iny:    DB $FD
        INC H
;flags set correctly by INC instruction in this case
        incPC

        ORG OP201
cmp_imm:
        nextbyte
        opCMP
        incPC

        ORG OP202
dex:    DB $FD
        DEC L
;flags set correctly by DEC instruction in this case
        incPC

        ORG OP203
wai:
        incPC           ;do nothing for now!

        ORG OP204
cpyA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opCPY
        incPC

        ORG OP205
cmpA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opCMP
        incPC

        ORG OP206
decA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opDEC
        incPC

        ORG OP207
        JP illegal

        ORG OP208
bne:
        INC DE
        JR Z,bne2
        EX AF,AF'
        nextbyteB
        LD L,(HL)
        LD A,L
        RLCA
        SBC A,A
        LD H,A
        ADD HL,DE       ;add displacement to PC (DE)
        EX DE,HL
        EX AF,AF'

bne2:
        incPC

        ORG OP209
cmp_zp_y:
        EX AF,AF'
        indirectY_read
        EX AF,AF'

        opCMP
        incPC

        ORG OP210
cmp_zp:
        get_ZP_read
        opCMP
        incPC

        ORG OP211
        JP illegal

        ORG OP212
        JP illegal2

        ORG OP213
cmpzpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opCMP  
        incPC

        ORG OP214
deczpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opDEC
        incPC

        ORG OP215
        JP illegal

        ORG OP216
cld:
        LD HL,vXflag
        RES 3,(HL)      ;clear D bit
cld2:
        LD HL,adcdaa2
        LD (HL),$00     ;set these routines to NOP (opcode 00) instead of DAA
        LD HL,sbcdaa2
        LD (HL),$00     ;preserve flags
        incPC

        ORG OP217
cmpay:
        EX AF,AF'
        getabsoluteY_read
        EX AF,AF'

        opCMP
        incPC

        ORG OP218
;PHX - push X
;no flags affected
phx:
        EXX
        EX AF,AF'
        DB $FD
        LD A,L
        LD (DE),A
        DEC E           ;SP is 0100-01FF only
        EX AF,AF'

        EXX
        incPC

        ORG OP219
stp:
        incPC           ;do nothing for now!

        ORG OP220
        JP illegal3

        ORG OP221
cmpax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opCMP
        incPC

        ORG OP222
decax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opDEC
        incPC
       
        ORG OP223
        JP illegal

        ORG OP224
cpx_imm:
        nextbyte
        opCPX
        incPC

        ORG OP225
sbc_zpx:
        EX AF,AF'
        indirectX_read
        EX AF,AF'

        opSBC
        incPC

        ORG OP226
        JP illegal2

        ORG OP227
        JP illegal

        ORG OP228
cpxzp:
        getZP_read
        opCPX
        incPC

        ORG OP229
sbczp:
        getZP_read
        opSBC
        incPC

        ORG OP230
inczp:
        getZP_read
        opINC
        incPC

        ORG OP231
        JP illegal

        ORG OP232
inx:    DB $FD
        INC L
;flags set correctly by INC instruction in this case
        incPC

        ORG OP233
sbc_imm:
        nextbyte

sharedsbc2:
;same comments as ADC for reasons of shared routine
;
;SBC on 6502 is quite different to Z80!
;it is effectively A-operand-(1-CF)
;so complement CF flag first!
        CCF
        SBC A,(HL)

;the following byte is modified by SED and CLD instructions
;to either NOP (CLD) or DAA (SED)

;and result of CF flag is opposite to Z80 too!
sbcdaa2:
        DAA ;patch daa/nop

;and result of CF flag is opposite to Z80 too!
        CCF
        LD HL,vVflag
        JP PO,$+8
;jump if overflow unset
;A=$40 if V set, A=0 if V reset
        LD (HL),$40
        incPC
;no overflow comes here
        LD (HL),$00
        incPC

        ORG OP234
nop:
        incPC           ;do nothing!

        ORG OP235
        JP illegal

        ORG OP236
cpxA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opCPX
        incPC

        ORG OP237
sbcA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opSBC
        incPC

        ORG OP238
incA:
        EX AF,AF'
        getabsolute_read
        EX AF,AF'

        opINC
        incPC

        ORG OP239
        JP illegal

        ORG OP240
beq:
        INC DE
        JR NZ,beq2
        EX AF,AF'
        nextbyteB
        LD L,(HL)
        LD A,L
        RLCA
        SBC A,A
        LD H,A
        ADD HL,DE       ;add displacement to PC (DE)
        EX DE,HL
        EX AF,AF'

beq2:
        incPC

        ORG OP241
sbc_zp_y:
        EX AF,AF'
        indirectY_read
        EX AF,AF'

        opSBC
        incPC

        ORG OP242
sbc_zp:
        get_ZP_read
        opSBC
        incPC

        ORG OP243
        JP illegal

        ORG OP244
        JP illegal2

        ORG OP245
sbczpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opSBC
        incPC

        ORG OP246
inczpx:
        EX AF,AF'
        getZPX_read
        EX AF,AF'

        opINC
        incPC

        ORG OP247
        JP illegal

        ORG OP248
sed:
        LD HL,vXflag
        SET 3,(HL)      ;set D bit
sed2:
        LD HL,adcdaa2
        LD (HL),$27     ;set these routines to DAA (opcode 27) instead of NOP
        LD HL,sbcdaa2
        LD (HL),$27     ;preserve flags
        incPC

        ORG OP249
sbcay:
        EX AF,AF'
        getabsoluteY_read
        EX AF,AF'

        opSBC
        incPC

        ORG OP250
;PLX - pull X
;sets Z and N flags
plx:
        EXX
        EX AF,AF'
        INC E
        LD A,(DE)
        LD B,A
        EXX
        EX AF,AF'

        INC B
        DEC B
        DB $FD
        LD L,B
        incPC

        ORG OP251
        JP illegal

        ORG OP252
        JP illegal3

        ORG OP253
sbcax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opSBC
        incPC

        ORG OP254
incax:
        EX AF,AF'
        getabsoluteX_read
        EX AF,AF'

        opINC
        incPC

        ORG OP255
        JP illegal

        ORG IM2page*256

;this is used as an IM2 vector table so we don't care if a kempston joystick is plugged in or not!

        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1,IM2page+1
        DB IM2page+1    ;257th byte goes here




        ORG Oppage*256
        DB OP0/256
        DB OP1/256
        DB OP2/256
        DB OP3/256
        DB OP4/256
        DB OP5/256
        DB OP6/256
        DB OP7/256
        DB OP8/256
        DB OP9/256
        DB OP10/256
        DB OP11/256
        DB OP12/256
        DB OP13/256
        DB OP14/256
        DB OP15/256
        DB OP16/256
        DB OP17/256
        DB OP18/256
        DB OP19/256
        DB OP20/256
        DB OP21/256
        DB OP22/256
        DB OP23/256
        DB OP24/256
        DB OP25/256
        DB OP26/256
        DB OP27/256
        DB OP28/256
        DB OP29/256
        DB OP30/256
        DB OP31/256
        DB OP32/256
        DB OP33/256
        DB OP34/256
        DB OP35/256
        DB OP36/256
        DB OP37/256
        DB OP38/256
        DB OP39/256
        DB OP40/256
        DB OP41/256
        DB OP42/256
        DB OP43/256
        DB OP44/256
        DB OP45/256
        DB OP46/256
        DB OP47/256
        DB OP48/256
        DB OP49/256
        DB OP50/256
        DB OP51/256
        DB OP52/256
        DB OP53/256
        DB OP54/256
        DB OP55/256
        DB OP56/256
        DB OP57/256
        DB OP58/256
        DB OP59/256
        DB OP60/256
        DB OP61/256
        DB OP62/256
        DB OP63/256
        DB OP64/256
        DB OP65/256
        DB OP66/256
        DB OP67/256
        DB OP68/256
        DB OP69/256
        DB OP70/256
        DB OP71/256
        DB OP72/256
        DB OP73/256
        DB OP74/256
        DB OP75/256
        DB OP76/256
        DB OP77/256
        DB OP78/256
        DB OP79/256
        DB OP80/256
        DB OP81/256
        DB OP82/256
        DB OP83/256
        DB OP84/256
        DB OP85/256
        DB OP86/256
        DB OP87/256
        DB OP88/256
        DB OP89/256
        DB OP90/256
        DB OP91/256
        DB OP92/256
        DB OP93/256
        DB OP94/256
        DB OP95/256
        DB OP96/256
        DB OP97/256
        DB OP98/256
        DB OP99/256
        DB OP100/256
        DB OP101/256
        DB OP102/256
        DB OP103/256
        DB OP104/256
        DB OP105/256
        DB OP106/256
        DB OP107/256
        DB OP108/256
        DB OP109/256
        DB OP110/256
        DB OP111/256
        DB OP112/256
        DB OP113/256
        DB OP114/256
        DB OP115/256
        DB OP116/256
        DB OP117/256
        DB OP118/256
        DB OP119/256
        DB OP120/256
        DB OP121/256
        DB OP122/256
        DB OP123/256
        DB OP124/256
        DB OP125/256
        DB OP126/256
        DB OP127/256
        DB OP128/256
        DB OP129/256
        DB OP130/256
        DB OP131/256
        DB OP132/256
        DB OP133/256
        DB OP134/256
        DB OP135/256
        DB OP136/256
        DB OP137/256
        DB OP138/256
        DB OP139/256
        DB OP140/256
        DB OP141/256
        DB OP142/256
        DB OP143/256
        DB OP144/256
        DB OP145/256
        DB OP146/256
        DB OP147/256
        DB OP148/256
        DB OP149/256
        DB OP150/256
        DB OP151/256
        DB OP152/256
        DB OP153/256
        DB OP154/256
        DB OP155/256
        DB OP156/256
        DB OP157/256
        DB OP158/256
        DB OP159/256
        DB OP160/256
        DB OP161/256
        DB OP162/256
        DB OP163/256
        DB OP164/256
        DB OP165/256
        DB OP166/256
        DB OP167/256
        DB OP168/256
        DB OP169/256
        DB OP170/256
        DB OP171/256
        DB OP172/256
        DB OP173/256
        DB OP174/256
        DB OP175/256
        DB OP176/256
        DB OP177/256
        DB OP178/256
        DB OP179/256
        DB OP180/256
        DB OP181/256
        DB OP182/256
        DB OP183/256
        DB OP184/256
        DB OP185/256
        DB OP186/256
        DB OP187/256
        DB OP188/256
        DB OP189/256
        DB OP190/256
        DB OP191/256
        DB OP192/256
        DB OP193/256
        DB OP194/256
        DB OP195/256
        DB OP196/256
        DB OP197/256
        DB OP198/256
        DB OP199/256
        DB OP200/256
        DB OP201/256
        DB OP202/256
        DB OP203/256
        DB OP204/256
        DB OP205/256
        DB OP206/256
        DB OP207/256
        DB OP208/256
        DB OP209/256
        DB OP210/256
        DB OP211/256
        DB OP212/256
        DB OP213/256
        DB OP214/256
        DB OP215/256
        DB OP216/256
        DB OP217/256
        DB OP218/256
        DB OP219/256
        DB OP220/256
        DB OP221/256
        DB OP222/256
        DB OP223/256
        DB OP224/256
        DB OP225/256
        DB OP226/256
        DB OP227/256
        DB OP228/256
        DB OP229/256
        DB OP230/256
        DB OP231/256
        DB OP232/256
        DB OP233/256
        DB OP234/256
        DB OP235/256
        DB OP236/256
        DB OP237/256
        DB OP238/256
        DB OP239/256
        DB OP240/256
        DB OP241/256
        DB OP242/256
        DB OP243/256
        DB OP244/256
        DB OP245/256
        DB OP246/256
        DB OP247/256
        DB OP248/256
        DB OP249/256
        DB OP250/256
        DB OP251/256
        DB OP252/256
        DB OP253/256
        DB OP254/256
        DB OP255/256

trap    EQU $01

        ORG M1page*256
;Opcode decode table

        DB $80
        DB $81
        DB $82 
        DB $83  ;VIC-20 had 1/2K at start of memory!
;3K RAM expansion table goes here!
        DB $84
        DB $85
        DB $86
        DB $87
        DB $88
        DB $89
        DB $8A
        DB $8B
        DB $8C
        DB $8D
        DB $8E
        DB $8F 
;4K base RAM goes here
        DB $90
        DB $91
        DB $92
        DB $93
        DB $94
        DB $95
        DB $96
        DB $97
        DB $98
        DB $99
        DB $9A
        DB $9B
        DB $9C
        DB $9D
        DB $9E
        DB $9F
;2000-7FFF unused memory
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
;character bit maps now (A000-AFFF)
        DB $A0
        DB $A1
        DB $A2
        DB $A3
        DB $A4
        DB $A5
        DB $A6
        DB $A7
        DB $A8
        DB $A9
        DB $AA
        DB $AB
        DB $AC
        DB $AD
        DB $AE
        DB $AF
;VIC chip (9000-93FF)
        DB $02  ;can't execute VIC registers!
        DB $02
        DB $02
        DB $02
;alternate colour nybble
        DB colourram+1          ;you can run code in nybble area if you want!
        DB colourram+2
;main colour nybble (in unexpanded VIC) (9600-97FF)
        DB colourram+1
        DB colourram+2
;more possible expansion RAM (9800-BFFF)
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
;CARTRIDGE AREA $A000-$BFFF
        DB $20
        DB $21
        DB $22
        DB $23
        DB $24
        DB $25
        DB $26
        DB $27
        DB $28
        DB $29
        DB $2A
        DB $2B
        DB $2C
        DB $2D
        DB $2E
        DB $2F
        DB $30
        DB $31
        DB $32
        DB $33
        DB $34
        DB $35
        DB $36
        DB $37
        DB $38
        DB $39
        DB $3A
        DB $3B
        DB $3C
        DB $3D
        DB $3E
        DB $3F
;BASIC2 ROM C000-DFFF
        DB $E0
        DB $E1
        DB $E2
        DB $E3
        DB $E4
        DB $E5
        DB $E6
        DB $E7
        DB $E8
        DB $E9
        DB $EA
        DB $EB
        DB $EC
        DB $ED
        DB $EE
        DB $EF
        DB $F0
        DB $F1
        DB $F2
        DB $F3
        DB $F4
        DB $F5
        DB $F6
        DB $F7
        DB $F8
        DB $F9
        DB $FA
        DB $FB
        DB $FC
        DB $FD
        DB $FE
        DB $FF
;KERNAL ROM E000-FFFF
        DB $60
        DB $61
        DB $62
        DB $63
        DB $64
        DB $65
        DB $66
        DB $67
        DB $68
        DB $69
        DB $6A
        DB $6B
        DB $6C
        DB $6D
        DB $6E
        DB $6F
        DB $70
        DB $71
        DB $72
        DB $73
        DB $74
        DB $75
        DB $76
        DB $77
        DB $78
        DB $79
        DB $7A
        DB $7B
        DB $7C
        DB $7D
        DB $7E
        DB $7F

;*** MEMORY READ TABLE ***
;MRpage
        DB $81
        DB $82 
        DB $83  ;VIC-20 had 1/2K at start of memory!
        DB $84
;3K RAM expansion table goes here!
        DB $85
        DB $86
        DB $87
        DB $88
        DB $89
        DB $8A
        DB $8B
        DB $8C
        DB $8D
        DB $8E
        DB $8F 
        DB $90
;4K base RAM goes here
        DB $91
        DB $92
        DB $93
        DB $94
        DB $95
        DB $96
        DB $97
        DB $98
        DB $99
        DB $9A
        DB $9B
        DB $9C
        DB $9D
        DB $9E
        DB $9F
        DB $A0
;2000-7FFF unused memory
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
;character bit maps now (A000-AFFF)
        DB $A1
        DB $A2
        DB $A3
        DB $A4
        DB $A5
        DB $A6
        DB $A7
        DB $A8
        DB $A9
        DB $AA
        DB $AB
        DB $AC
        DB $AD
        DB $AE
        DB $AF
        DB $B0
;VIC chip (9000-93FF)
        DB trap         ;VIC chip
        DB trap         ;VIA chip
        DB $02
        DB $02
;alternate colour nybble
        DB colourram+1
        DB colourram+2
;main colour nybble (in unexpanded VIC) (9600-97FF)
        DB colourram+1
        DB colourram+2
;more possible expansion RAM (9800-BFFF)
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
;CARTRIDGE AREA $A000-$BFFF
        DB $21
        DB $22
        DB $23
        DB $24
        DB $25
        DB $26
        DB $27
        DB $28
        DB $29
        DB $2A
        DB $2B
        DB $2C
        DB $2D
        DB $2E
        DB $2F
        DB $30
        DB $31
        DB $32
        DB $33
        DB $34
        DB $35
        DB $36
        DB $37
        DB $38
        DB $39
        DB $3A
        DB $3B
        DB $3C
        DB $3D
        DB $3E
        DB $3F
        DB $40
;BASIC2 ROM C000-DFFF
        DB $E1
        DB $E2
        DB $E3
        DB $E4
        DB $E5
        DB $E6
        DB $E7
        DB $E8
        DB $E9
        DB $EA
        DB $EB
        DB $EC
        DB $ED
        DB $EE
        DB $EF
        DB $F0
        DB $F1
        DB $F2
        DB $F3
        DB $F4
        DB $F5
        DB $F6
        DB $F7
        DB $F8
        DB $F9
        DB $FA
        DB $FB
        DB $FC
        DB $FD
        DB $FE
        DB $FF
        DB $00  ;this really should be $00 so it wraps around to $FF
;KERNAL ROM E000-FFFF
        DB $61
        DB $62
        DB $63
        DB $64
        DB $65
        DB $66
        DB $67
        DB $68
        DB $69
        DB $6A
        DB $6B
        DB $6C
        DB $6D
        DB $6E
        DB $6F
        DB $70
        DB $71
        DB $72
        DB $73
        DB $74
        DB $75
        DB $76
        DB $77
        DB $78
        DB $79
        DB $7A
        DB $7B
        DB $7C
        DB $7D
        DB $7E
        DB $7F
        DB $80

;*** MEMORY WRITE TABLE ***
;MWpage
        DB $81
        DB $82 
        DB $83  ;VIC-20 had 1/2K at start of memory!
        DB $84
;3K RAM expansion table goes here!
        DB $85
        DB $86
        DB $87
        DB $88
        DB $89
        DB $8A
        DB $8B
        DB $8C
        DB $8D
        DB $8E
        DB $8F 
        DB $90
;4K base RAM goes here
        DB $91
        DB $92
        DB $93
        DB $94
        DB $95
        DB $96
        DB $97
        DB $98
        DB $99
        DB $9A
        DB $9B
        DB $9C
        DB $9D
        DB $9E
        DB $9F          ;trap writes to screen done via VIC9005!
        DB $A0
;2000-7FFF unused memory
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02  ;set this to $03 if you want +8K RAM
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
;character bit maps (A000-AFFF)
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
;VIC chip (9000-93FF)
        DB trap         ;VIC chip
        DB trap         ;VIA chip
        DB $02
        DB $02
;alternate colour nybble
        DB trap
        DB trap
;main colour nybble (in unexpanded VIC) (9600-97FF)
        DB trap
        DB trap
;more possible expansion RAM (9800-BFFF)
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
;BASIC2 ROM C000-DFFF
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
;KERNAL ROM E000-FFFF
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
        DB $02
M1pagesize=$-(M1page*256)

        ORG (IM2page*256)+257
;after IM2 IRQ vector table
;now use some of this blank space between $B901 and $B9B9

RAMwrite_trap:

;To get to this point the 6502 has tried to write to a page which
;is marked "to be trapped". This usually means I/O or screen memory space.
;
;Some routines just update a RAM copy of a hardware register. Therefore HL is modified from a 6502 address
;to a Z80 address and the 6502 instruction works on the "real" Z80 address
;
;Others require "post-processing" such as writing to the screen. To do this IX is modified to point to another routine.
;This routine is called after the 6502 instruction has been run.
;An example is the colourRAM routine. Any changes to colour RAM are implemented by changing IX to point to "attr2".
;The attr2 routine then updates the Spectrum screen to reflect the change.


;on entry
;hi-byte of trapped address is in L
;lo-byte of trapped address is in C

;when finished
;hi-byte of new address is taken from A
;lo-byte of new address is taken from C

;DON'T NEED TO SAVE AF WITH RAMTRAPs AS WE'VE ALREADY PAGED OUT AF

        LD A,L                  ;get hi-byte of trapped address
        AND A                   ;set SF flag, is it >=$80xx (ie: VIA or VIC?)
        JP M,writeIO            ;skip if so

;otherwise assume it's the screen
        LD IX,WriteScreen       ;draw screen once opcode has been done
        ADD A,ScreenPage-$1E    ;adjust msb so virtual 1E00 goes to real RAM address ($9E00)
        LD H,A                  ;make HL back to what it was
        LD L,C
        LD (RAMtrap),HL         ;make note of addr used
        RET                     ;back to WRITE memory operation!



irq_handler:

;maskable interrupt (IRQ) has been generated
;so push PC and flags on stack
;clear BCD mode (like CMOS 6502)
;and jump to contents of $FFFE

;first un-modify the opcode_decode routine to clear the IRQ

        LD HL,(M1page*256)+$26  ;LD H,$M1page instead of JR $-4
                                ;$26 is opcode for LD H,nn
        LD (op_decode+1),HL     ;reset 2 bytes to what they normally are

;PC (DE) is already correct at this point - so stack it
        EX AF,AF' ;'

        LD A,D
        do_pushA
        LD A,E
        do_pushA
        LD HL,FFrealpage*256+$FE        ;validate it!
        LD E,(HL)
        INC L           ;safe to do as we know HL has to be FFFE - therfore validated page still valid
                        ;also L will never overflow so quicker to do INC L than INC HL
        LD D,(HL)       ;DE (PC) now contains  vector

        EX AF,AF' ;'
        opPHP           ;prepare flags in A
        AND $EF         ;force B flag to be clear (only IRQ pushes this bit as 0)
        do_pushA        ;correct flags are in A

        LD HL,vXflag
        LD A,(HL)
        AND $E3         ;clear bits 4,3 and 2 (B,D and I)
        OR $04          ;have to set bit 2 (I flag)
        LD (HL),A
irq_finish:
        EX AF,AF' ;'

        JP (IX)         ;no incPC as DE (PC) is already correct


;process BREAK as RESTORE key
kkey10:
        LD A,$FE
        IN A,($FE)              ;CAPS pressed?
        RRCA
kkey11:
        RET C           ;exit if not (C) or if is (NC)
       ld a,0xf7
       in a,(0xfe)
       rra ;'1'+cs+space
       jp nc,quit
        LD HL,irq1
        LD A,(HL)
        XOR $08         ;JR C or JR NC
        LD (HL),A
        LD HL,kkey11
        LD A,(HL)       ;see if we're RET C or RET NC
        XOR $08         ;D0 or D8
        LD (HL),A
        CP $D8          ;=RET C
        RET Z           ;exit if so because we can now accept a new BREAK press

kkey12:
        LD HL,VIA911E   ;IER to see if CA1 is enabled
        BIT 1,(HL)      ;bit 1 controls CA1
        RET Z           ;if not set then CA1 has been disabled
                        ;therefore no restore key
        DEC HL          ;IFR (911D) shows where irq came from
        LD A,(HL)
        OR $82          ;set bits 7 (irq flag) and 1 (CA1 irq)
        LD (HL),A       ;NMI generate by IM2 routine
        JP signalNMI    ;CALL/RET

markIRQ:
        LD A,(op_decode+1)
        CP $18
        JR Z,irq_di             ;IRQ can't override NMI
        LD A,$18                ;opcode for JR
        LD (op_decode+1),A      ;force interrupt to be serviced once
        LD A,irqMD              ;displacement value to get to irqM routine
        LD (op_decode+2),A
        JP irq_di

        ORG (IM2page*256)+257+IM2page
;interrupt handler at XYXY address, eg:$B9B9

;check for restore (generate NMI regardless of SEI/CLI setting)
        PUSH AF                 ;preserve flags
        LD A,$7F
        IN A,($FE)              ;SPACE pressed?
        RRCA
irq1:
        JR C,irq2
        PUSH HL
        CALL kkey10
        POP HL
irq2:
        LD A,(vXflag)
        AND $04                 ;get I flag
        JR NZ,irq_di            ;if set then interrupts are disabled

        LD A,(VIA912E)          ;VIA D2IER
        AND $40                 ;is TIMER1 enabled?
        JP NZ,markIRQ           ;if so then get ready to process IRQ

irq_di:

        POP AF
        EI
        RET


        ORG Safepage
;this table is page aligned for speed

VIC9000:
        DB $00
VIC9001:
        DB $00
VIC9002:
        DB $00
VIC9003:
        DB $00
VIC9004:
        DB $00
VIC9005:
        DB $00
VIC9006:
        DB $00
VIC9007:
        DB $00
VIC9008:
        DB $00
VIC9009:
        DB $00
VIC900A:
        DB $00
VIC900B:
        DB $00
VIC900C:
        DB $00
VIC900D:
        DB $00
VIC900E:
        DB $00
VIC900F:
        DB $00
VIA9110:
        DB $00
VIA9111:
        DB $00
VIA9112:
        DB $00
VIA9113:
        DB $00
VIA9114:
        DB $00
VIA9115:
        DB $00
VIA9116:
        DB $00
VIA9117:
        DB $00
VIA9118:
        DB $00
VIA9119:
        DB $00
VIA911A:
        DB $00
VIA911B:
        DB $00
VIA911C:
        DB $00
VIA911D:
        DB $00
VIA911E:
        DB $00
VIA911F:
        DB $00
VIA9120:
        DB $00
VIA9121:
        DB $00
VIA9122:
        DB $00
VIA9123:
        DB $00
VIA9124:
        DB $00
VIA9125:
        DB $00
VIA9126:
        DB $00
VIA9127:
        DB $00
VIA9128:
        DB $00
VIA9129:
        DB $00
VIA912A:
        DB $00
VIA912B:
        DB $00
VIA912C:
        DB $00
VIA912D:
        DB $00
VIA912E:
        DB $00
VIA912F:
        DB $00

;safe gap to stop code being overwritten (AND $3F used so possible to overwrite $30-$3F)
        DB $00,$00,$00,$00
        DB $00,$00,$00,$00
        DB $00,$00,$00,$00
        DB $00,$00,$00,$00

IOread:
;table used for when READING from VIC and VIA registers

        DW VICthru      ;$9000 - VIC
        DW VICthru
        DW VICthru
        DW VICraster03  ;bit 7 contains lsb bit of raster
        DW VICraster04  ;contains bits 1-8 of raster
        DW VICthru
        DW VICthru
        DW VICthru
        DW VICthru      ;08
        DW VICthru
        DW VICthru
        DW VICthru
        DW VICthru
        DW VICthru
        DW VICthru
        DW VICthru      ;0F
;VIA1
        DW VIAthru      ;$9110 +00      D1ORB
        DW D1ORA        ;+01
        DW VIAthru
        DW VIAthru
        DW D1T1lsb      ;TIMER1 counter lsb
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW D1ORA        ;0F (non latching)
;VIA2
        DW D2ORB        ;10 - $9120
        DW D2ORA        ;11
        DW VIAthru
        DW VIAthru
        DW D2T1lsb      ;TIMER1 counter lsb
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW VIAthru
        DW D2ORA        ;1F (non latching)

VICthru:
;VIC and VIA just read from RAM copy of register
;they are the same for now
       
VIAthru:
;as MSB of VIAthru is safe as MSB of Safepage
;H is already correct
;and as C is already correct, just ...
        RET

D2ORA:
;attempt to read from keyboard
        XOR A
        IN A,($FE)              ;scan all keys at once
        CPL
        AND $1F                 ;convert FF (no keys) to 00 - setting Z flag
        JR Z,keyQ2              ;if Z then no key pressed so quickly exit

;clear previous key states
        PUSH DE
        LD HL,vkeypage          ;vkeypage always starts on a page boundary
        LD B,$08
key0:   LD (HL),$FF
        INC L
        DJNZ key0

;read in ZX keyboard (with sym-shift processing)
readZXkeys:
        CALL keyrows

;now read in which VIC key columns should be read (D2ORB) and only process them
        LD A,(VIA9120)          ;D2ORB
        LD D,A
        LD A,$FF
        LD HL,vkeypage
        LD B,$08
keyskip1:
        RRC D
        JR C,keyskip2           ;only merge in if column was required
        AND (HL)
keyskip2:
        INC L                   ;vkeypage is page aligned
        DJNZ keyskip1
        POP DE

keyQ:
        LD HL,VIA9121           ;D2ORA
        LD (HL),A
        LD C,L          ;H already correct from VIA9121
        RET             ;back to READ memory operation!
keyQ2:
        LD HL,VIA9121           ;D2ORA
        LD (HL),$FF             ;no keys
        LD C,L          ;H already correct from VIA9121
        RET             ;back to READ memory operation!

keyrows:
        LD BC,$7FFE     ;port
        IN A,(C)
        LD B,C          ;set port to $FEFE ready for main loop
        LD HL,keydata
        RRCA
        RRCA            ;is SYM pressed?
        JP C,key1       ;skip if not
        LD HL,keydata+$0050     ;80 bytes = 2x40 keys
key1:
        IN A,(C)
        PUSH BC
        LD B,$05
key2:
        RRCA
        JP NC,keypressed
        INC HL
key3:
        INC HL
        DJNZ key2
        POP BC
        RLC B
        JP C,key1
       
        IN A,(C)        ;BC=$FEFE at this point
        RRCA            ;is CAPS pressed?
        RET C           ;finished if not

        LD A,$EF        ;check for CAPS+number
        IN A,($FE)
        OR $E0          ;mask out top 3 bits
        CP $FF
        JR NZ,key4      ;deal with 6-0
        LD A,$7F
        IN A,($FE)      ;check space ,ie: BREAK
        RRCA
        JP C,keyshiftonly

        LD HL,vkeypage+4
        SET 0,(HL)      ;mark SPACE as not pressed
        DEC L           ;onto vkeypage+3
        RES 0,(HL)      ;mark RUN/STOP as pressed
        RET
keyshiftonly:
        LD HL,vkeypage+3
        RES 1,(HL)      ;mark LEFT SHIFT as pressed
        RET
key4:
        RRCA            ;deal with CAPS+0
        JR C,keyshiftonly
        LD HL,vkeypage          ;deal with col 0
        RES 7,(HL)              ;mark DELETE as pressed
        LD HL,vkeypage+7        ;0 is in col 7
        SET 4,(HL)              ;mark 0 as not pressed
        RET


keypressed:
        LD E,(HL)
        INC HL
        LD D,(vkeypage/256)
        LD C,A          ;preserve A
        LD A,(DE)       ;get existing key state for VIC20 column
        AND (HL)        ;change key status as per table
        LD (DE),A
        LD A,C          ;restore A
        JP key3         ;quicker than CALL/RET
       
keydata:
        DB $09,$FF      ;we don't do caps here
        DB 4,$FD
        DB 3,$FB
        db 4,$FB        ;c
        db 3,$F7        ;v
        db 2,$FD        ;a
        db 5,$FD
        db 2,$FB
        db 5,$FB
        db 2,$F7        ;g
        db 6,$FE        ;q
        db 1,$FD
        db 6,$FD
        db 1,$FB
        db 6,$FB
        db 0,$FE ;1
        db 7,$FE
        db 0,$FD
        db 7,$FD
        db 0,$FB        ;5
        db 7,$EF        ;0
        db 0,$EF
        db 7,$F7
        db 0,$F7
        db 7,$FB        ;6
        db 1,$DF        ;p
        db 6,$EF
        db 1,$EF
        db 6,$F7
        db 1,$F7        ;y
        db 1,$7F ;enter
        db 2,$DF
        db 5,$EF
        db 2,$EF
        db 5,$F7        ;h
        db 4,$FE        ;space
        db $09,$ff      ;we don't do sym here
        db 4,$EF
        db 3,$EF
        db 4,$F7        ;b
;now with SYM
        db 9,$ff
        db 5,$DF        ;':'
        db 0,$BF        ;'г'
        db 5,$FE        ;'CBM'
        db 3,$BF        ;'/'
        db 3,$FE        ;'run/STOP'     row 2
        db 9,$ff
        db 9,$ff
        db 9,$ff
        db 9,$ff
        db 9,$ff        ;row 3
        db 9,$ff
        db 9,$ff
        db 1,$FE        ;'left arrow'
        db 9,$ff
        db 4,$7F        ;F1 row 4
        db 6,$DF        ;"@"
        db 5,$7F        ;F3
        db 7,$BF        ;HOME key
        db 6,$7F        ;F5
        db 9,$ff        ;row 5
        db 9,$ff
        db 2,$7F        ;RIGHT cursor key
        db 7,$7F        ;F7
        db 3,$7F        ;DOWN cursor key
        db 9,$ff        ;row 6
        db 2,$BF        ;';'
        db 9,$ff
        db 9,$ff
        db 9,$ff
        db 9,$ff        ;row 7
        db 5,$BF        ;"="
        db 0,$DF        ;'+'
        db 7,$DF        ;'-'
        db 6,$BF        ;'^'
        db 9,$ff        ;row 8
        db 9,$ff
        db 4,$DF        ;'.'
        db 3,$DF        ;','
        db 1,$BF        ;'*'


RAMread_trap:
;on entry
;hi-byte of trapped address is in L
;lo-byte of trapped address is in C

;when finished
;hi-byte of new address is taken from H
;lo-byte of new address is taken from C

;ONLY I/O DEVICES ARE TRAPPED WHEN READING

        LD A,C
        AND $3F
        ADD A,A         ;double up as 2 bytes per entry
                        ;A would be transferred to BC
        ADD A,IOread%256        ;effectively do HL+BC
        LD L,A          ;lsb first
        LD H,IOread/256 ;msb next - as A must be <$3F and IOread must be $40 bytes into a page
                        ;(safepage starts on a page boundary), (A*2)+$40 can never overflow from lsb into msb
        LD B,(HL)
        INC L           ;quicker to use INC L than INC HL - always in same page
        LD H,(HL)
        LD L,B          ;routine address in HL

        JP (HL)         ;jump to routine


D1ORA:
;read joystick
        IN A,($1F)      ;read kempston joystick (ZX)
        RRA             ;ignore bit 0 (RIGHT)
        AND $0F         ;only want FUDL bits
        LD HL,Kempston_Table
        ADD A,L
        LD L,A          ;let's hope HL+15 doesn't wrap around a page!!
        LD C,(HL)
        LD HL,VIA9113   ;DDR on VIA 1, port A
        LD A,(HL)       ;get which bits are input (=0) and output (=1)
        CPL             ;we're interested in input bits, so flip
        AND C           ;and only keep joystick bits if they're input enabled
        LD C,A
        LD L,VIA9111%256        ;H already correct
        LD A,(HL)       ;joystick is bits 5,4,3,2
        AND $C3         ;so only keep bits 7,6,1,0 of existing VIA value
        OR C            ;merge in joystick
        LD (HL),A       ;store it in RAM copy of register
        LD C,L          ;H already setup
        RET

D2ORB:
;read right bit of joystick
        LD HL,VIA9122           ;VIA2 DDRB
        LD C,VIA9120%256        ;H is already correct
        BIT 7,(HL)
        RET NZ          ;DDR says this port is for output if bit 7 set
                        ;so no joystick check - therefore skip
        IN A,($1F)      ;kempston joystick
        CPL             ;flip bits
        AND $01
        RRCA            ;joystick R bit into bit 7
        LD B,A
        LD A,(HL)
        AND $7F
        OR B            ;merge in joystick
        LD (HL),A       ;store in VIA9120 (VIA 2 IO PORT B)
        RET             ;H & C already set correctly

D2T1lsb:
        LD HL,VIA9124   ;lsb of T1
        JR randomT1

D1T1lsb:
        LD HL,VIA9114   ;lsb of T1
randomT1:
        LD A,R
        RLA             ;R doesn't set bit 7 randomly, so let's assume CF is random here!
        LD (HL),A
        LD C,L          ;H already correct
        RET


vtemp2:
        DB $00

complain:
        LD A,$06
        OUT ($FE),A
        JR complain



writeIO:

;RAM Write trap continues here
;at this point A=msb of trapped address (copy of L register)
;and C=lsb of trapped address

;when finished
;hi-byte of new address is taken from H
;lo-byte of new address is taken from C

;only traps >= $8000 come here - everything else goes to screen write!

        CP $94
        JR C,writeIO2   ;we've not trapped a colour RAM write so skip attr1 routine

attr1:
;in this case HL should be between $9400-$97FF (colour RAM)
        LD IX,attr2
        AND $01                 ;A is 0 or 1 depending on it being $94/96 or $95/97
        ADD A,colourram         ;adjust HL so it points to host's
        LD H,A                  ;address of colour RAM
        LD L,C
        LD (RAMtrap),HL         ;6502 will be working on real copy of colour RAM
        RET

writeIO2:
        CP $90
        JR Z,VICchipOUT         ;if not $90 then we must be writing to $91xx (VIA)

VIAout:
        LD A,C                  ;get lo-byte of original address back
        AND $3F
        LD C,A                  ;store safe (0-3F) version of lsb back in C
        LD H,Safepage/256       ;write into RAM copy of h/w register
                                ;H and C now correct
        CPL                     ;flip bits in A
        AND $0F                 ;is it 911F or 912F?
        CP $0F
        RET C                   ;exit if not

;       CP $0E
;       RET C                   ;finished unless we're talking +0E or +0F
       
;       CP $0F
;       JR NZ,VIAout2
        LD A,C                  ;deal with 911F and 912F
        AND $F1                 ;force $1F to 11 and $2F to 21
        LD C,A                  ;update C
        RET                     ;done!
       
VIAout2:
;deal with IER of each VIA
        LD A,C
        AND $20                 ;are we dealing with VIA2
;       JR Z,VIAout3            ;jump if so

;       LD IX,DealWithIER1
        RET                     ;done! (H & C already correct)

VIAout3:
;       LD IX,DealWithIER2
        RET                     ;done! (H & C already correct)

DealWithIER2:
        LD HL,VIA912E
        JP dealwithIER

DealWithIER1:
        LD HL,VIA911E

dealwithIER:
        EX AF,AF' ;'
        LD IX,op_decode
        LD A,(BC)
        BIT 7,A
        JR Z,clearIER
;set IER:

        OR (HL)                 ;merge in existing IER
        LD (HL),A
        EX AF,AF' ;'            ;back to 6502 AF
        JP (IX)
clearIER:

        CPL
        AND (HL)                ;reset bits which were set to 1
        OR $80                  ;bit 7 is always set when reading back in
        LD (HL),A
        EX AF,AF' ;'            ;back to 6502 AF
        JP (IX)


VICchipOUT:
        LD A,C
        AND $0F                 ;keep to 00-0F
        LD C,A                  ;update C (lsb)
        ADD A,A                 ;double up VIC register (00-0F becomes 00-1E)
        LD HL,VICtable          ;VICtable doesn't cross page boundary at any point
        ADD A,L                 ;add lsb of VICtable to A
        LD L,A                  ;which is lsb for HL
        LD A,(HL)               ;can't corrupt C so use A instead
        INC HL
        LD B,(HL)

        DB $DD
        LD H,B                  ;copy B/A into IX
        DB $DD                  ;as IX routine will deal with the write request
        LD L,A 

        LD H,Safepage/256       ;write into RAM copy of h/w register
                                ;C (lsb) is already correct
        RET

rVIC900F:
        EX AF,AF' ;'            ;save 6502 AF
        LD A,(VIC900F)
        LD H,A
        AND $08
        OR $30
        LD (attr4b),a           ;$30=jr nc, $38=jr c
                                ;default is to JR C,attr6 (effectively jr to attr5)
VIC900Fb:
        LD A,H
        AND $F0                 ;only need top 4 bits of $900F for background colour
        LD L,A

        LD H,attrs/256
        LD A,(HL)               ;convert VIC value (bbbb0fff) into ZX ATTR value
        LD HL,VICbackg
        CP (HL)                 ;has colour changed?
        LD (HL),A               ;set background colour
        CALL NZ,refreshattr     ;refresh whole screen if colour has changed
                                ;and carry on into border routine either way

VIC900Fd:
;sort out border
        LD IX,op_decode
        LD A,(VIC900F)          ;get original $900F register contents back
        AND $07                 ;keep lower bits 0-2 which has border in it

        LD L,A
        ADD A,A
        ADD A,A
        ADD A,A
        ADD A,A                 ;multiply by 16 to make paper colour (upper) = border (lower ink)
        OR L                    ;add original ink back in (ink=paper here)
        LD L,A
        LD H,attrs/256
        LD A,(HL)               ;convert VIC colour to Speccy colour
        AND $3F                 ;filter out BRIGHT/FLASH attributes as border can't be bright
        LD (VIC9002d+1),A
        AND $07
        OUT ($FE),A

        LD A,(WriteScreenSize+1)
        NEG                     ;convert back to 0-31
        LD L,A
        JP VIC9002b1            ;don't need to alter screensize so skip first bit
                                ;this routine draws the "inner border".

rVIC9002:
;number of columns
;also manipulates colourRAM location

        EX AF,AF' ;'            ;save 6502 AF
        LD IX,op_decode
        LD BC,$0102
        LD A,(VIC9002)
        AND $80
        RLCA
        RLCA                    ;A now =0 or =2
        ADD A,$94               ;colourRAM base msb now in A                           
        LD L,A
        LD H,MWpage
        LD (HL),B               ;=trap!
        INC L
        LD (HL),B               ;=trap!
;       XOR $02                 ;trap might not always be =2, but this needs to be =2 for it to work
        XOR C                   ;change A to the opposite base
        LD L,A
        LD (HL),C               ;=dump to ROM (unused)
        INC L
        LD (HL),C               ;=dump to ROM (unused)

        LD A,(VIC9002)
        AND $1F                 ;ignore bit 7 and keep column width as 1-31

VIC9002b:
        LD L,A          ;width of screen in L
        NEG             ;before we negate it
        LD (WriteScreenSize+1),A
                        ;adjust VIC column size as required
VIC9002b1:
        LD H,$58
        LD BC,$0020

VIC9002c:
        BIT 5,L         ;have we done all 31 cols yet? (we have if we're now at 32)
        JR NZ,VIC9002e  ;skip if so

;draw vertical lines
VIC9002d:
        LD (HL),$00     ;this value is changed as required
        ADD HL,BC
        LD A,H
        CP $5B          ;have we gone outside attr area?
        JR NZ,VIC9002d  ;keep going until we do
        LD H,$58        ;top of attr again
        INC L           ;next column
        JP VIC9002c     ;loop until done!

;draw horizontal rows
VIC9002e:
        LD A,$18        ;24 ZX rows
        LD HL,write0-1
        SUB (HL)        ;subtract number of VIC rows from 24
        JR Z,VIC9002h   ;no border rows to draw (row depth =24) so skip
        LD B,A          ;we've got B number of rows to do false border in
        LD HL,$5B00
        LD A,(VIC9002d+1)       ;VIC border colour

VIC9002f:
        PUSH BC
        LD B,$20        ;32 cols
VIC9002g:
        DEC HL
        LD (HL),A
        DJNZ VIC9002g
        POP BC
        DJNZ VIC9002f
VIC9002h:
        JP rVIC9005b    ;process start of screen memory (also affected by bit 7 of $9002)

rVIC9003:
;number of rows
        EX AF,AF' ;'    ;save 6502 AF
        LD A,(VIC9003)
        AND $7F         ;drop bit 7
        RRA             ;div by 2 (CF will be cleared by above AND)
                        ;CF now shows whether we're in 8 or 16 high characters
        LD HL,writescreen4
        JR NC,rVIC9003_8
rVIC9003_16:
        LD (HL),$29     ;opcode for ADD HL,HL - needed for 16 high characters
        LD HL,attr316
        LD (HL),$87     ;ADD A,A
        LD HL,display7bytes16
        JR VIC9003a
rVIC9003_8:
        LD (HL),$00
        LD HL,attr316
        LD (HL),$00     ;NOP
        LD HL,display7bytes8
VIC9003a:
        PUSH DE
        LD DE,write3
        LDI
        LDI             ;do this for 7 bytes
        LDI    
        LDI
        LDI
        LDI
        LDI
        LDI             ;NEEDS TO BE FOR 8 BYTES NOW DUE TO EXTRA AF,AF'
        POP DE
        DEC A           ;setting rows to zero upsets screendraw routine!
                        ;0-24 to 255-23
        CP $18          ;only up to 24 rows allowed
        JR C,VIC9003b
        LD A,$17        ;force to 23+1 rows
VIC9003b:
        INC A           ;bring back to 1-24
        LD (write0-1),A ;alter gfx routine to display correct number of rows
        LD (write1+1),A
        LD (attr3-1),A  ;and attribute routine too
        LD (attr4a+1),A
        CALL refreshattr
        LD IX,op_decode
        JP VIC9002e     ;exit via column routine to draw new false border
                        ;only need to draw rows because columns can't have changed via $9003


rVIC9005:
        EX AF,AF' ;'    ;save 6502 AF

;deal with charset pointer first

        LD A,(VIC9005)
        LD B,$0F
        AND B                   ;0-15 only
        LD HL,CharsetTranslation-$0F00  ;compensate for BC being $0Fxx
        LD C,A
        ADD HL,BC
        LD A,(HL)       ;get new value for charset
        LD HL,WriteScreenSize-$02
        INC (HL)                ;force an update
        LD (WriteScreenCharset+2),A     ;adjust character set pointer as required


rVIC9005b:

;now deal with screen memory
;  X = ($9005) AND 112
;  Y = ($9002) AND 128
;  Address = 4*Y + 64*X
;
;    X       Y         Address
;
;    128       0             0          0
;    128     128          $200        512
;    144       0          $400       1024
;    144     128          $600       1536
;    160       0          $800       2048
;    160     128          $A00       2560
;    176       0          $C00       3072
;    176     128          $E00       3584
;    192       0         $1000       4096
;    192     128         $1200       4608
;    208       0         $1400       5120
;    208     128         $1600       5632
;    224       0         $1800       6144
;    224     128         $1A00       6656
;    240       0         $1C00       7168
;    240     128         $1E00       7680

        LD HL,(oldVDUtrap)      ;address in MW table that was altered
        LD BC,(oldVDUrambytes)  ;previous contents of those addresses
        LD (HL),C
        INC L
        LD (HL),B               ;effectively untrap them
        LD A,(VIC9002)
        RLCA                    ;move bit 7 into C flag
        LD A,(VIC9005)
        RRA             ;take bit 7 of $9002 into account
        RRCA            ;shift significant nybble into
        RRCA
        RRCA            ;least significant nybble
        AND $1F         ;keep screen memory bits only (32 different values)
        LD C,A
        LD HL,screenmemorymap
        LD B,$00
        ADD HL,BC
        LD A,(HL)                       ;page to be trapped (also page+1)
        LD HL,WriteScreenSize-$02
        CP (HL)                         ;have we changed? (this code is also called from elsewhere)
        LD (HL),A
        CALL NZ,RedrawScreen            ;if we have then redraw entire screen
        SUB ZPrealpage                  ;adjust to 6502 memory space (ie:$90 = ZX real address, convert to $10 = VIC real address)
        LD L,A
        LD H,MWpage
        LD (oldVDUtrap),HL
        LD C,(HL)
        LD (HL),trap            ;mark as trap
        INC L
        LD B,(HL)
        LD (HL),trap            ;mark as trap
        LD (oldVDUrambytes),BC
        EX AF,AF' ;'            ;restore 6502 AF
        LD IX,op_decode
        JP (IX)

VICraster04:
        LD HL,VIC9004   ;point to bits 1-8 of raster
        LD C,L
        JP VICraster03b ;use same routine as raster03 in case software only reads $9004
                        ;we've still got to increase $9004 and keep it in range

VICraster03:
        LD HL,VIC9003
        LD A,(HL)
        ADD A,$80
        LD (HL),A
        LD C,L          ;H already correct
        RET NC          ;finished if bit 0 of raster went from 0 to 1
        INC L           ;HL to point to VIC9004 (rest of raster)

VICraster03b:
        INC (HL)        ;increase VIC9004 (rest of raster)
        RET P           ;finished if 0-127 (which is 0-255 for raster)
        LD A,(HL)
        CP $9C          ;max value for raster is 311, so we need to check against 312/2
        RET C           ;finished if we're under
        LD (HL),$00     ;back to 0
        RET


rVICsoundvol:
;sound volume
        EX AF,AF' ;'    ;save 6502 AF
rVICsoundvol1:
        LD IX,op_decode
        LD BC,$FFFD     ;port $FFFD - sound port on 128
        LD H,$08        ;R8 - vol A
        OUT (C),H
        LD B,$BF        ;want port $BFFD now
        LD A,(VIC900E)
        AND $0F         ;keep to 0-15
        OUT (C),A       ;set volume

        INC H
        LD B,$FF        ;port $FFFD again
        OUT (C),H
        LD B,$BF        ;want port $BFFD now
        OUT (C),A       ;set volume on channel B

        INC H
        LD B,$FF        ;port $FFFD again
        OUT (C),H
        LD B,$BF        ;want port $BFFD now
        OUT (C),A       ;set volume on channel C

;now turn channels on/off

        LD BC,$8000
        LD HL,VIC900D
        LD A,(HL)
        AND B                   ;is sound channel enabled?
        JR Z,rVICsoundvol2      ;0=off
        INC C                   ;set bit 0. This will be shifted into bit 3.
                                ;noise is on channel A.

rVICsoundvol2:
        SLA C
        DEC HL          ;VIC900C

        LD A,(HL)
        AND B                   ;is sound channel enabled?
        JR Z,rVICsoundvol3      ;0=off
        INC C

rVICsoundvol3:
        SLA C
        DEC HL          ;VIC900B

        LD A,(HL)
        AND B                   ;is sound channel enabled?
        JR Z,rVICsoundvol4      ;0=off
        INC C

rVICsoundvol4:
        SLA C
        DEC HL          ;VIC900A

        LD A,(HL)
        AND B                   ;is sound channel enabled?
        JR Z,rVICsoundvol5      ;0=off
        INC C

rVICsoundvol5:
        LD A,C          ;channels on/off in H now
        CPL             ;flip because 0=on on the AY chip!
        LD H,A
        LD BC,$FFFD     ;sound port on 128
        LD A,$07        ;R7 - tone/noise selection
        OUT (C),A
        LD B,$BF        ;want port $BFFD now
        OUT (C),H       ;enable all 3 tone channels and possibly noise channel on A

        EX AF,AF' ;'    ;restore 6502 AF
        JP (IX)

rVICsoundA:
;IX set via rVICsoundvol
        EX AF,AF' ;'    ;save 6502 AF
        LD A,(VIC900A)
        CPL             ;255 is highest note
        LD L,A
        LD H,$00
        LD B,H
        LD C,L          ;copy HL into BC
        ADD HL,HL       ;x2
        ADD HL,BC       ;x3
        ADD HL,HL       ;x6
        ADD HL,HL       ;x12
        ADD HL,BC       ;x13
        ADD HL,HL       ;x26
                        ;real value should be 25.6, but this is close enough!
        LD BC,$FFFD
        XOR A           ;R0 - lsb of tone for channel A
        OUT (C),A       ;select register of 128 sound chip
        LD B,$BF        ;port $BFFD
        OUT (C),L       ;out lsb of period
        LD B,$FF
        INC A           ;R1 - msb of tone
        OUT (C),A
        LD B,$BF
        OUT (C),H       ;out msb of period
        JP rVICsoundvol1        ;if not then channel should be silent!
                                ;so just set volume to 0 via this routine


rVICsoundB:
;IX set via rVICsoundvol
        EX AF,AF' ;'    ;save 6502 AF
        LD A,(VIC900B)
        CPL
        LD L,A
        LD H,$00
        LD B,H
        LD C,L          ;copy HL into BC
        ADD HL,HL       ;x2
        ADD HL,BC       ;x3
        ADD HL,HL       ;x6
        ADD HL,HL       ;x12
        ADD HL,BC       ;x13
                        ;real value should be 12.8, but this is close enough!
        LD BC,$FFFD
        LD A,$02        ;R2 - lsb of tone for channel B
        OUT (C),A       ;select register of 128 sound chip
        LD B,$BF        ;port $BFFD
        OUT (C),L       ;out lsb of period
        LD B,$FF
        INC A           ;R3 - msb of tone
        OUT (C),A
        LD B,$BF
        OUT (C),H       ;out msb of period
        JP rVICsoundvol1        ;if not then channel should be silent!
                                ;so just set volume to 0 via this routine

rVICsoundC:
;IX set via rVICsoundvol
        EX AF,AF' ;'    ;save 6502 AF
        LD A,(VIC900C)
        CPL
        LD L,A
        LD H,$00
        LD B,H
        SRL A           ;halve A
        LD C,A          ;so half HL is in BC
        ADD HL,BC       ;x1.5
        ADD HL,HL       ;x3
        ADD HL,HL       ;x6
        ADD HL,BC       ;x6.5
                        ;real value should be 6.4, but this is close enough!
        LD BC,$FFFD
        LD A,$04        ;R4 - lsb of tone for channel C
        OUT (C),A       ;select register of 128 sound chip
        LD B,$BF        ;port $BFFD
        OUT (C),L       ;out lsb of period
        LD B,$FF
        INC A           ;R5 - msb of tone
        OUT (C),A
        LD B,$BF
        OUT (C),H       ;out msb of period
        JP rVICsoundvol1        ;if not then channel should be silent!
                                ;so just set volume to 0 via this routine

rVICsoundD:
;IX set via rVICsoundvol
        EX AF,AF' ;'    ;save 6502 AF
        LD A,(VIC900D)
        CPL             ;255 is highest note
        LD L,A
        LD H,$00
        LD B,H
        LD C,L          ;copy HL into BC

        ADD HL,HL       ;x2
        ADD HL,BC       ;x3
        ADD HL,HL       ;x6
        ADD HL,HL       ;x12
        ADD HL,BC       ;x13

;       LD A,L          ;keep only lsb of result as noise channel only has 5-bit resolution on AY
        SRL L           ;halve A
        SRL L           ;quarter A as quarter of 13 = 3.25
                        ;real value should be 3.2, but this is close enough!

        LD BC,$FFFD
        LD A,$06        ;R6 - tone for noise channel
        OUT (C),A       ;select register of 128 sound chip
        LD B,$BF        ;port $BFFD
        OUT (C),L       ;out lsb of period
        JP rVICsoundvol1

refreshattr:
        LD HL,colourram*256     ;start at top left
        LD IX,refreshattr2
        PUSH AF                 ;save our AF because both AF and AF' will be destroyed
refreshattr1:
        PUSH HL                 ;remember which attr square we're updating for later
        PUSH DE                 ;preserve DE

        JP attr30B              ;refresh attr
;JP (IX) comes back here with 6502 AF paged in and POP DE already done
refreshattr2:
        POP HL                  ;get which attr square we're updating back
        INC HL
        LD A,colourram+2        ;done all 512? (2 pages = 512 bytes)
        CP H
        JP NZ,refreshattr1
        POP AF                  ;restore 6502 AF as current AF
        RET

RedrawScreen:
        PUSH AF
        LD H,A                  ; H will contain 'screenpage'
        LD L,$00
        LD IX,redraw2
        LD BC,$0200
redraw1:
        PUSH BC
        PUSH HL
        JP WriteDirect
redraw2:
        POP HL
        INC HL
        POP BC
        DEC BC
        LD A,B
        OR C
        JR NZ,redraw1
        POP AF
        RET

;FOLLOWING IS BASED ON GEOFF WEARMOUTH'S ROM DISASSEMBLY
;It is not verified to work yet (on real hardware)
;It allos you to load a headerless block of code into VIC RAM space from 0000 upwards

; ------------------------------------
; Load header or block of information
; ------------------------------------
;   This routine is used to load bytes and on entry A is set to $00 for a
;   header or to $FF for data.  IX points to the start of receiving location
;   and DE holds the length of bytes to be loaded. If, on entry the carry flag
;   is set then data is loaded, if reset then it is verified.

;; LD-BYTES
L0556:  INC     D               ; reset the zero flag without disturbing carry.
        EX      AF,AF' ;'          ; preserve entry flags.
        DEC     D               ; restore high byte of length.

        DI                      ; disable interrupts

        LD      A,$0F           ; make the border white and mic off.
        OUT     ($FE),A         ; output to port.


;   the reading of the EAR bit (D6) will always be preceded by a test of the
;   space key (D0), so store the initial post-test state.

        IN      A,($FE)         ; read the ear state - bit 6.
        RRA                     ; rotate to bit 5.
        AND     $20             ; isolate this bit.
        OR      $02             ; combine with red border colour.
        LD      C,A             ; and store initial state long-term in C.
        CP      A               ; set the zero flag.

;

;; LD-BREAK
L056B:  RET     NZ              ; return if at any time space is pressed.

;; LD-START
L056C:  CALL    L05E7           ; routine LD-EDGE-1
        JR      NC,L056B        ; back to LD-BREAK with time out and no
                                ; edge present on tape.

;   but continue when a transition is found on tape.

        LD      HL,$0415        ; set up 16-bit outer loop counter for
                                ; approx 1 second delay.

;; LD-WAIT
L0574:  DJNZ    L0574           ; self loop to LD-WAIT (for 256 times)

        DEC     HL              ; decrease outer loop counter.
        LD      A,H             ; test for
        OR      L               ; zero.
        JR      NZ,L0574        ; back to LD-WAIT, if not zero, with zero in B.

;   continue after delay with H holding zero and B also.
;   sample 256 edges to check that we are in the middle of a lead-in section.

        CALL    L05E3           ; routine LD-EDGE-2
        JR      NC,L056B        ; back to LD-BREAK
                                ; if no edges at all.

;; LD-LEADER
L0580:  LD      B,$9C           ; set timing value.
        CALL    L05E3           ; routine LD-EDGE-2
        JR      NC,L056B        ; back to LD-BREAK if time-out

        LD      A,$C6           ; two edges must be spaced apart.
        CP      B               ; compare
        JR      NC,L056C        ; back to LD-START if too close together for a
                                ; lead-in.

        INC     H               ; proceed to test 256 edged sample.
        JR      NZ,L0580        ; back to LD-LEADER while more to do.

;   sample indicates we are in the middle of a two or five second lead-in.
;   Now test every edge looking for the terminal sync signal.

;; LD-SYNC
L058F:  LD      B,$C9           ; initial timing value in B.
        CALL    L05E7           ; routine LD-EDGE-1
        JR      NC,L056B        ; back to LD-BREAK with time-out.

        LD      A,B             ; fetch augmented timing value from B.
        CP      $D4             ; compare
        JR      NC,L058F        ; back to LD-SYNC if gap too big, that is,
                                ; a normal lead-in edge gap.

;   but a short gap will be the sync pulse.
;   in which case another edge should appear before B rises to $FF

        CALL    L05E7           ; routine LD-EDGE-1
        RET     NC              ; return with time-out.

; proceed when the sync at the end of the lead-in is found.
; We are about to load data so change the border colours.

        LD      A,C             ; fetch long-term mask from C
        XOR     $03             ; and make blue/yellow.

        LD      C,A             ; store the new long-term byte.

        LD      H,$00           ; set up parity byte as zero.
        LD      B,$B0           ; timing.
        JR      L05C8           ; forward to LD-MARKER
                                ; the loop mid entry point with the alternate
                                ; zero flag reset to indicate first byte
                                ; is discarded.

; --------------
;   the loading loop loads each byte and is entered at the mid point.

;; LD-LOOP
L05A9:  EX      AF,AF' ;'          ; restore entry flags and type in A.
        JR      NZ,L05B3        ; forward to LD-FLAG if awaiting initial flag
                                ; which is to be discarded.

        JR      NC,L05BD        ; forward to LD-VERIFY if not to be loaded.

        EX AF,AF' ;'
        LD B,MRpage             ;this allows us to write ROM cart area
        DB $DD
        LD C,H
        LD A,(BC)               ;translated page
        DEC A                   ;now has correct value
        LD B,A
        DB $DD
        LD C,L                  ;ZX address in BC
        EX AF,AF' ;'
        LD A,L                  ;byte loaded from tape (must keep!)
        LD (BC),A               ; place loaded byte at memory location.
        JR L05C2           ; forward to LD-NEXT

; ---

;; LD-FLAG
;first byte is 00 for header, FF for data
;last byte is parity
L05B3:  RL      C               ; preserve carry (verify) flag in long-term
                                ; state byte. Bit 7 can be lost.

        XOR     L               ; compare type in A with first byte in L.
        RET     NZ              ; return if no match e.g. CODE vs. DATA.

;   continue when data type matches.

        LD      A,C             ; fetch byte with stored carry
        RRA                     ; rotate it to carry flag again
        LD      C,A             ; restore long-term port state.

        JR      L05C5           ; forward to LD-DEC.
                                ; but why not to location after ?


;;LD-VERIFY
L05BD:

;; LD-NEXT
L05C2:  INC     IX              ; increment byte pointer.

;; LD-DEC
L05C4:  DEC     DE              ; decrement length.

L05C5:
        EX      AF,AF' ;'          ; store the flags.
        LD      B,$B2           ; timing.

;   when starting to read 8 bits the receiving byte is marked with bit at right.
;   when this is rotated out again then 8 bits have been read.

;; LD-MARKER
L05C8:  LD      L,$01           ; initialize as %00000001

;; LD-8-BITS
;returns byte loaded in L
L05CA:  CALL    L05E3           ; routine LD-EDGE-2 increments B relative to
                                ; gap between 2 edges.
        RET     NC              ; return with time-out.

        LD      A,$CB           ; the comparison byte.
        CP      B               ; compare to incremented value of B.
                                ; if B is higher then bit on tape was set.
                                ; if <= then bit on tape is reset.

        RL      L               ; rotate the carry bit into L.

        LD      B,$B0           ; reset the B timer byte.
        JP      NC,L05CA        ; JUMP back to LD-8-BITS

;   when carry set then marker bit has been passed out and byte is complete.

        LD      A,H             ; fetch the running parity byte.
        XOR     L               ; include the new byte.
        LD      H,A             ; and store back in parity register.

        LD      A,D             ; check length of
        OR      E               ; expected bytes.
        JR      NZ,L05A9        ; back to LD-LOOP
                                ; while there are more.

;   when all bytes loaded then parity byte should be zero.

        LD      A,H             ; fetch parity byte.
        CP      $01             ; set carry if zero.
        RET                     ; return
                                ; in no carry then error as checksum disagrees.
; -------------------------
; Check signal being loaded
; -------------------------
;   An edge is a transition from one mic state to another.
;   More specifically a change in bit 6 of value input from port $FE.
;   Graphically it is a change of border colour, say, blue to yellow.
;   The first entry point looks for two adjacent edges. The second entry point
;   is used to find a single edge.
;   The B register holds a count, up to 256, within which the edge (or edges)
;   must be found. The gap between two edges will be more for a '1' than a '0'
;   so the value of B denotes the state of the bit (two edges) read from tape.

; ->

;; LD-EDGE-2
L05E3:  CALL    L05E7           ; call routine LD-EDGE-1 below.
        RET     NC              ; return if space pressed or time-out.
                                ; else continue and look for another adjacent
                                ; edge which together represent a bit on the
                                ; tape.

; ->
;   this entry point is used to find a single edge from above but also
;   when detecting a read-in signal on the tape.

;; LD-EDGE-1
L05E7:  LD      A,$16           ; a delay value of twenty two.

;; LD-DELAY
L05E9:  DEC     A               ; decrement counter
        JR      NZ,L05E9        ; loop back to LD-DELAY 22 times.

        AND      A              ; clear carry.

;; LD-SAMPLE
L05ED:  INC     B               ; increment the time-out counter.
        RET     Z               ; return with failure when $FF passed.

        LD      A,$7F           ; prepare to read keyboard and EAR port
        IN      A,($FE)         ; row $7FFE. bit 6 is EAR, bit 0 is SPACE key.
        RRA                     ; test outer key the space. (bit 6 moves to 5)
        RET     NC              ; return if space pressed.  >>>

        XOR     C               ; compare with initial long-term state.
        AND     $20             ; isolate bit 5
        JR      Z,L05ED         ; back to LD-SAMPLE if no edge.

;   but an edge, a transition of the EAR bit, has been found so switch the
;   long-term comparison byte containing both border colour and EAR bit.

        LD      A,C             ; fetch comparison value.
        CPL                     ; switch the bits
        LD      C,A             ; and put back in C for long-term.

        AND     $07             ; isolate new colour bits.
        OR      $08             ; set bit 3 - MIC off.
        OUT     ($FE),A         ; send to port to effect the change of colour.

        SCF                     ; set carry flag signaling edge found within
                                ; time allowed.
        RET                     ; return.


VICLOAD:
        PUSH IX
        PUSH DE
        PUSH AF
        LD IX,$0000
        LD DE,$4000
        LD A,$FF
        SCF
        CALL L0556
        POP AF
        POP DE
        POP IX
        EI
        LD DE,$E5B5     ;VIC KERNAL panic
        JP (IX)         ;back to 6502

        ORG $1450+base
VICtable:
;MUST NOT STRADDLE A PAGE BOUNDARY!
;list of routines that deal with each register of VIC chip
;when WRITING to register
        DW VICignore
        DW VICignore
        DW rVIC9002     ;col width
        DW rVIC9003     ;row depth
        DW VICignore
        DW rVIC9005     ;charmap pointer
        DW VICignore
        DW VICignore
        DW VICignore
        DW VICignore
        DW rVICsoundA   ;channel A
        DW rVICsoundB   ;channel B
        DW rVICsoundC   ;channel C
        DW VICignore
        DW rVICsoundvol ;sound volume
        DW rVIC900F     ;border colour

VICignore:
        LD IX,op_decode
        EX AF,AF' ;'            ;restore 6502 AF
        JP (IX)         ;ignore value and carry on!

;was org $0e00+base

        ORG $1490+base
CharsetTranslation:
;safe place for CHARSET translation table
;subtract 240 from POKE $9005,x value
        DB chargen/256
        DB (chargen/256)+4
        DB (chargen/256)+8
        DB (chargen/256)+12
        DB $00          ;would be VIC i/o space
        DB colourram    ;colour RAM
        DB $00
        DB $00          ;these 2 would do nothing anyway
        DB ZPrealpage   ;page 0!
        DB ZPrealpage+4 ;1024
        DB ZPrealpage+8 ;2048
        DB ZPrealpage+12        ;3192
        DB ZPrealpage+16
        DB ZPrealpage+20
        DB ZPrealpage+24
        DB ZPrealpage+28        ;7168
screenmemorymap:
;take bits 7-4 of $9005. Rotate right
;move bit 7 of $9002 into bit 7 of result
        DB 0                    ;$8000
        DB 0
        DB 0
        DB 0
        DB 0
        DB 0
        DB 0
        DB 0
        DB ZPrealpage
        DB ZPrealpage+4
        DB ZPrealpage+8
        DB ZPrealpage+12
        DB ZPrealpage+16
        DB ZPrealpage+20
        DB ZPrealpage+24
        DB ZPrealpage+28
        DB 0                    ;$8200
        DB 0
        DB 0
        DB 0
        DB 0
        DB 0
        DB 0
        DB 0
        DB ZPrealpage+2
        DB ZPrealpage+6
        DB ZPrealpage+10
        DB ZPrealpage+14
        DB ZPrealpage+18
        DB ZPrealpage+22
        DB ZPrealpage+26
        DB ZPrealpage+30
oldVDUtrap:
        DW vkeypage             ;address in MW table that was altered
                                ;fill with dummy address to stop it altering $0000/$01
oldVDUrambytes:
        DW $00                  ;previous contents of those addresses


        ORG base+$1000

;this is a safe blank area to use that's on a page boundary
vkeypage:
        DB $00
        DB $00
        DB $00
        DB $00
        DB $00
        DB $00
        DB $00
        DB $00


        ORG base+$08C6
;kempston table - converts IN31 to VIC equivalent
;this 16-byte table must not cross a page boundary!
;it doesn't have to be page aligned though!

;don't need bit 0 of IN 31 (RIGHT) as that's in a separate VIC register

Kempston_Table:
        DB $FF  ;nothing pressed (all values INVERTED)
        DB $EF  ;L (bit 4 on VIC)
        DB $F7  ;D (bit 3 on VIC)
        DB $E7  ;LD
        DB $FB  ;U (bit 2 on VIC)
        DB $EB  ;LU
        DB $F3  ;UD
        DB $E3  ;LUD
        DB $DF  ;F      (bit 5 on VIC)
        DB $CF  ;FL
        DB $D7  ;FD
        DB $C7  ;FLD
        DB $D3  ;FU
        DB $CB  ;FLU
        DB $D3  ;FUD
        DB $C3  ;FLUD

        ORG base+$2600
;this occupies last page before MR,MW,M1,IRQroutine,IM2 tables
rowtable:
        DW $4000
        DW $4020
        DW $4040
        DW $4060
        DW $4080
        DW $40A0
        DW $40C0
        DW $40E0
        DW $4800
        DW $4820
        DW $4840
        DW $4860
        DW $4880
        DW $48A0
        DW $48C0
        DW $48E0
        DW $5000
        DW $5020
        DW $5040
        DW $5060
        DW $5080
        DW $50A0
        DW $50C0
        DW $50E0
;repeat list of addresses again in case somebody sets row depth >24
        DW $4000
        DW $4020
        DW $4040
        DW $4060
        DW $4080
        DW $40A0
        DW $40C0
        DW $40E0
        DW $4800
        DW $4820
        DW $4840
        DW $4860
        DW $4880
        DW $48A0
        DW $48C0
        DW $48E0


VICborder:
        DB $00
VICbackg:
        DB $00
VICforeg:
        DB $00


        ORG base+$2500
attrs:
;       R   G   B
;                       needs to go Ggg Rrr Bb
;black  00  00  00
;       000 000 00 = $00        = 000 000 00 = $00
;white  FF  FF  FF
;       111 111 11 = $FF        = 111 111 11 = $FF
;red    B4  18  18
;       110 001 00 = $C4        = 001 110 00 = $38
;cyan   4C  E6  D8
;       010 110 10 = $5A        = 110 010 10 = $CA
;purple BC  29  CA
;       110 001 10 = $C6        = 001 110 10 = $3A
;green  42  E4  36
;       010 110 01 = $59        = 110 010 01 = $C9
;blue   32  2A  C8
;       001 001 10 = $2C        = 001 001 10 = $26
;yellow D2  E1  26
;       110 110 01 = $D9        = 110 110 01 = $D9
;orange CA  5A  02
;       110 011 00 = $CC        = 011 110 00 = $78
;l oran DE  AC  80
;       110 101 10 = $D6        = 101 110 10 = $BA
;pink   DC  94  94
;       110 100 10 = $D2        = 100 110 10 = $9A
;l cyan A5  F4  EC
;       101 111 11 = $BF        = 111 101 11 = $F7
;l purp E0  9A  E4
;       110 101 11 = $D7        = 101 110 11 = $BB
;l grn  A0  F2  9A
;       101 111 10 = $BE        = 111 101 10 = $F6
;l blue 9C  92  E4
;       101 100 11 = $B3        = 100 101 11 = $97
;l yel  EF  F8  9A
;       111 111 10 = $FE        = 111 111 10 = $FE



;8 cols is black,white,red,cyan,purple,green,blue,yellow

        ORG attrs
        DB 0    ; p=blk i=blk
        DB 7    ; p=blk i=wht
        DB 2    ; p=blk i=red
        DB 5    ; p=blk i=cyn
        DB 3    ; p=blk i=pur
        DB 4    ; p=blk i=grn
        DB 1    ; p=blk i=blu
        DB 6    ; p=blk i=yel
        DB 6    ; p=blk i=ora
        DB 70   ; p=blk i=l ora
        DB 66   ; p=blk i=l pnk
        DB 69   ; p=blk i=l cyn
        DB 67   ; p=blk i=l pur
        DB 68   ; p=blk i=l grn
        DB 65   ; p=blk i=l blu
        DB 70   ; p=blk i=l yel
        DB 56   ; p=wht i=blk
        DB 63   ; p=wht i=wht
        DB 58   ; p=wht i=red
        DB 61   ; p=wht i=cyn
        DB 59   ; p=wht i=pur
        DB 60   ; p=wht i=grn
        DB 57   ; p=wht i=blu
        DB 62   ; p=wht i=yel
        DB 62   ; p=wht i=ora
        DB 126  ; p=wht i=l ora
        DB 122  ; p=wht i=l pnk
        DB 125  ; p=wht i=l cyn
        DB 123  ; p=wht i=l pur
        DB 124  ; p=wht i=l grn
        DB 121  ; p=wht i=l blu
        DB 126  ; p=wht i=l yel
        DB 16   ; p=red i=blk
        DB 23   ; p=red i=wht
        DB 18   ; p=red i=red
        DB 21   ; p=red i=cyn
        DB 19   ; p=red i=pur
        DB 20   ; p=red i=grn
        DB 17   ; p=red i=blu
        DB 22   ; p=red i=yel
        DB 22   ; p=red i=ora
        DB 86   ; p=red i=l ora
        DB 83   ; p=red i=l pnk clash
        DB 85   ; p=red i=l cyn
        DB 83   ; p=red i=l pur
        DB 84   ; p=red i=l grn
        DB 81   ; p=red i=l blu
        DB 86   ; p=red i=l yel
        DB 40   ; p=cyn i=blk
        DB 47   ; p=cyn i=wht
        DB 42   ; p=cyn i=red
        DB 45   ; p=cyn i=cyn
        DB 43   ; p=cyn i=pur
        DB 44   ; p=cyn i=grn
        DB 41   ; p=cyn i=blu
        DB 46   ; p=cyn i=yel
        DB 46   ; p=cyn i=ora
        DB 110  ; p=cyn i=l ora
        DB 106  ; p=cyn i=l pnk
        DB 77   ; p=cyn i=l cyn
        DB 107  ; p=cyn i=l pur
        DB 108  ; p=cyn i=l grn
        DB 105  ; p=cyn i=l blu
        DB 110  ; p=cyn i=l yel
        DB 24   ; p=pur i=blk
        DB 31   ; p=pur i=wht
        DB 26   ; p=pur i=red
        DB 29   ; p=pur i=cyn
        DB 27   ; p=pur i=pur
        DB 28   ; p=pur i=grn
        DB 25   ; p=pur i=blu
        DB 30   ; p=pur i=yel
        DB 30   ; p=pur i=ora
        DB 94   ; p=pur i=l ora
        DB 90   ; p=pur i=l pnk
        DB 93   ; p=pur i=l cyn
        DB 93   ; p=pur i=l pur clash
        DB 92   ; p=pur i=l grn
        DB 89   ; p=pur i=l blu
        DB 94   ; p=pur i=l yel
        DB 32   ; p=grn i=blk
        DB 39   ; p=grn i=wht
        DB 34   ; p=grn i=red
        DB 37   ; p=grn i=cyn
        DB 35   ; p=grn i=pur
        DB 36   ; p=grn i=grn
        DB 33   ; p=grn i=blu
        DB 38   ; p=grn i=yel
        DB 38   ; p=grn i=ora
        DB 102  ; p=grn i=l ora
        DB 98   ; p=grn i=l pnk
        DB 101  ; p=grn i=l cyn
        DB 99   ; p=grn i=l pur
        DB 101  ; p=grn i=l grn clash
        DB 97   ; p=grn i=l blu
        DB 102  ; p=grn i=l yel
        DB 8    ; p=blu i=blk
        DB 15   ; p=blu i=wht
        DB 10   ; p=blu i=red
        DB 13   ; p=blu i=cyn
        DB 11   ; p=blu i=pur
        DB 12   ; p=blu i=grn
        DB 9    ; p=blu i=blu
        DB 14   ; p=blu i=yel
        DB 14   ; p=blu i=ora
        DB 78   ; p=blu i=l ora
        DB 74   ; p=blu i=l pnk
        DB 77   ; p=blu i=l cyn
        DB 75   ; p=blu i=l pur
        DB 76   ; p=blu i=l grn
        DB 77   ; p=blu i=l blu clash
        DB 78   ; p=blu i=l yel
        DB 48   ; p=yel i=blk
        DB 55   ; p=yel i=wht
        DB 50   ; p=yel i=red
        DB 53   ; p=yel i=cyn
        DB 51   ; p=yel i=pur
        DB 52   ; p=yel i=grn
        DB 49   ; p=yel i=blu
        DB 54   ; p=yel i=yel
        DB 50   ; p=yel i=ora clash
        DB 114  ; p=yel i=l ora clash
        DB 114  ; p=yel i=l pnk
        DB 117  ; p=yel i=l cyn
        DB 115  ; p=yel i=l pur
        DB 116  ; p=yel i=l grn
        DB 113  ; p=yel i=l blu
        DB 119  ; p=yel i=l yel clash
        DB 48   ; p=ora i=blk
        DB 55   ; p=ora i=wht
        DB 50   ; p=ora i=red
        DB 53   ; p=ora i=cyn
        DB 51   ; p=ora i=pur
        DB 52   ; p=ora i=grn
        DB 49   ; p=ora i=blu
        DB 22   ; p=ora i=yel clash
        DB 54   ; p=ora i=ora
        DB 86   ; p=ora i=l ora clash
        DB 114  ; p=ora i=l pnk
        DB 117  ; p=ora i=l cyn
        DB 115  ; p=ora i=l pur
        DB 116  ; p=ora i=l grn
        DB 113  ; p=ora i=l blu
        DB 22   ; p=ora i=l yel clash
        DB 112  ; p=l ora i=blk
        DB 119  ; p=l ora i=wht
        DB 114  ; p=l ora i=red
        DB 117  ; p=l ora i=cyn
        DB 115  ; p=l ora i=pur
        DB 116  ; p=l ora i=grn
        DB 113  ; p=l ora i=blu
        DB 86   ; p=l ora i=yel clash
        DB 86   ; p=l ora i=ora clash
        DB 118  ; p=l ora i=l ora
        DB 114  ; p=l ora i=l pnk
        DB 117  ; p=l ora i=l cyn
        DB 115  ; p=l ora i=l pur
        DB 116  ; p=l ora i=l grn
        DB 113  ; p=l ora i=l blu
        DB 119  ; p=l ora i=l yel clash
        DB 80   ; p=l pnk i=blk
        DB 87   ; p=l pnk i=wht
        DB 83   ; p=l pnk i=red clash
        DB 85   ; p=l pnk i=cyn
        DB 83   ; p=l pnk i=pur
        DB 84   ; p=l pnk i=grn
        DB 81   ; p=l pnk i=blu
        DB 86   ; p=l pnk i=yel
        DB 86   ; p=l pnk i=ora
        DB 86   ; p=l pnk i=l ora
        DB 82   ; p=l pnk i=l pnk
        DB 85   ; p=l pnk i=l cyn
        DB 83   ; p=l pnk i=l pur
        DB 84   ; p=l pnk i=l grn
        DB 81   ; p=l pnk i=l blu
        DB 86   ; p=l pnk i=l yel
        DB 104  ; p=l cyn i=blk
        DB 111  ; p=l cyn i=wht
        DB 106  ; p=l cyn i=red
        DB 105  ; p=l cyn i=cyn clash
        DB 107  ; p=l cyn i=pur
        DB 108  ; p=l cyn i=grn
        DB 105  ; p=l cyn i=blu
        DB 110  ; p=l cyn i=yel
        DB 110  ; p=l cyn i=ora
        DB 110  ; p=l cyn i=l ora
        DB 106  ; p=l cyn i=l pnk
        DB 109  ; p=l cyn i=l cyn
        DB 107  ; p=l cyn i=l pur
        DB 108  ; p=l cyn i=l grn
        DB 105  ; p=l cyn i=l blu
        DB 110  ; p=l cyn i=l yel
        DB 88   ; p=l pur i=blk
        DB 95   ; p=l pur i=wht
        DB 90   ; p=l pur i=red
        DB 93   ; p=l pur i=cyn
        DB 93   ; p=l pur i=pur clash
        DB 92   ; p=l pur i=grn
        DB 89   ; p=l pur i=blu
        DB 94   ; p=l pur i=yel
        DB 94   ; p=l pur i=ora
        DB 94   ; p=l pur i=l ora
        DB 90   ; p=l pur i=l pnk
        DB 93   ; p=l pur i=l cyn
        DB 91   ; p=l pur i=l pur
        DB 92   ; p=l pur i=l grn
        DB 89   ; p=l pur i=l blu
        DB 94   ; p=l pur i=l yel
        DB 96   ; p=l grn i=blk
        DB 103  ; p=l grn i=wht
        DB 98   ; p=l grn i=red
        DB 101  ; p=l grn i=cyn
        DB 99   ; p=l grn i=pur
        DB 101  ; p=l grn i=grn clash
        DB 97   ; p=l grn i=blu
        DB 102  ; p=l grn i=yel
        DB 102  ; p=l grn i=ora
        DB 102  ; p=l grn i=l ora
        DB 98   ; p=l grn i=l pnk
        DB 101  ; p=l grn i=l cyn
        DB 99   ; p=l grn i=l pur
        DB 100  ; p=l grn i=l grn
        DB 97   ; p=l grn i=l blu
        DB 102  ; p=l grn i=l yel
        DB 72   ; p=l blu i=blk
        DB 79   ; p=l blu i=wht
        DB 74   ; p=l blu i=red
        DB 77   ; p=l blu i=cyn
        DB 75   ; p=l blu i=pur
        DB 76   ; p=l blu i=grn
        DB 72   ; p=l blu i=blu clash
        DB 78   ; p=l blu i=yel
        DB 78   ; p=l blu i=ora
        DB 78   ; p=l blu i=l ora
        DB 74   ; p=l blu i=l pnk
        DB 77   ; p=l blu i=l cyn
        DB 75   ; p=l blu i=l pur
        DB 76   ; p=l blu i=l grn
        DB 73   ; p=l blu i=l blu
        DB 78   ; p=l blu i=l yel
        DB 112  ; p=l yel i=blk
        DB 119  ; p=l yel i=wht
        DB 114  ; p=l yel i=red
        DB 117  ; p=l yel i=cyn
        DB 115  ; p=l yel i=pur
        DB 116  ; p=l yel i=grn
        DB 113  ; p=l yel i=blu
        DB 119  ; p=l yel i=yel clash
        DB 114  ; p=l yel i=ora clash
        DB 114  ; p=l yel i=l ora clash
        DB 114  ; p=l yel i=l pnk
        DB 117  ; p=l yel i=l cyn
        DB 115  ; p=l yel i=l pur
        DB 116  ; p=l yel i=l grn
        DB 113  ; p=l yel i=l blu
        DB 118  ; p=l yel i=l yel

;#END
        ;display "begin=",begin
end=(M1page*256)+M1pagesize
        display "end=",end


        savebin "vic20.com",begin,end-begin

        LABELSLIST "../../us/user.l"