; Apple 1 emulator for SAM Coupe, by Simon Owen (v1.0) ; ; WWW: http://simonowen.com/sam/apple1emu/ base: equ &b000 ; Spare-ish Apple 1 space status: equ 249 ; Status and extended keyboard port lmpr: equ 250 ; Low Memory Page Register hmpr: equ 251 ; High Memory Page Register vmpr: equ 252 ; Video Memory Page Register keyboard: equ 254 ; Keyboard port border: equ 254 ; Border port rom0_off: equ %00100000 ; LMPR bit to disable ROM0 rom1_on: equ %01000000 ; LMPR bit to enable ROM1 vmpr_mode2: equ %00100000 ; Mode 2 select for VMPR low_page: equ 3 ; LMPR during emulation screen_page: equ 5 ; SAM display file_page: equ 6 ; File import text page bord_stp: equ 2 ; STP instruction halted CPU (red) bord_wai: equ 6 ; WAI instruction waiting for interrupt (yellow) m6502_nmi: equ &fffa ; nmi vector address m6502_reset: equ &fffc ; reset vector address m6502_int: equ &fffe ; int vector address (also for BRK) getkey: equ &1cab ; SAM ROM key reading (NZ=got key in A, A=0 for no key) ; Apple 1 keyboard and display I/O locations kbd_data: equ &d010 ; keyboard data kbd_ctrl: equ &d011 ; keyboard control dsp_data: equ &d012 ; display data dsp_ctrl: equ &d013 ; display control ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org base dump $ autoexec start: di ld a,low_page+rom0_off out (lmpr),a ld a,vmpr_mode2+screen_page out (vmpr),a ld sp,stack_top call reorder_decode ; optimise instruction decode table call set_sam_attrs ; set the mode 2 attrs so the screen is visible call setup_im2 ; enable IM 2 reset_loop: ld hl,0 ld (dsp_data),hl ; display ready ld (kbd_ctrl),hl ; no key available ld hl,(m6502_reset) ; start from reset vector ld (reg_pc),hl ei call execute ; GO! jr reset_loop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Utility functions ; Fill SAM mode 2 display attributes to make the screen visible set_sam_attrs: ld a,screen_page+rom0_off out (lmpr),a ld hl,&2000 ld bc,&1844 ; 24 blocks of bright green on black clear_lp: ld (hl),c inc l jr nz,clear_lp inc h djnz clear_lp ld a,low_page+rom0_off out (lmpr),a ret ; Scroll the screen up 1 row and clear the bottom line scroll_screen: ld hl,0 ld d,h ld e,l inc h ld bc,&1700 scroll_lp: FOR 32, ldi ; 32 * LDI jp pe,scroll_lp dec h scroll_clr: ld (hl),0 inc l jp nz,scroll_clr ret ; Advance the cursor 1 character, wrapping and scrolling if necessary advance_chr: ld hl,(cursor_xy) cp &5f jr z,back_chr cp &0a jr z,advance_line cp &0d jr z,advance_line ld a,l add a,6 ld l,a cp &f0 jr c,no_wrap advance_line: ld l,0 inc h ld a,h cp &18 jr nz,no_wrap dec h exx call scroll_screen exx no_wrap: ld (cursor_xy),hl ret back_chr: ld a,l sub 6 ld l,a jr nc,no_wrap ld l,39*6 ld a,h sub 1 adc a,0 ld h,a jr no_wrap ; Map display character from SAM to Apple 1 map_chr: cp &20 jr c,invalid_chr and %10111111 xor %01100000 ret invalid_chr: xor a ret ; Display character in A at current cursor position display_chr: add a,a ; * 2 add a,a ; * 4 ld l,a ld h,0 add hl,hl ; * 8 ld de,font_data add hl,de ld d,mask_data/256 exx ld hl,(cursor_xy) ld a,l and %00000111 srl l srl l srl l inc l exx ld e,a exx ld de,&0020 ld b,8 draw_lp: exx ld c,(hl) inc l xor a cp e jr z,no_rot ld b,e rot_lp: srl c rra djnz rot_lp no_rot: ld b,a ld a,(de) inc e exx and (hl) inc l exx or c ex af,af' ld a,(de) dec e exx and (hl) exx or b exx ld (hl),a dec l ex af,af' ld (hl),a add hl,de djnz draw_lp ret ; LSB=pixel position (0-240), MSB=display row (0-23) cursor_xy: defw 0 ; Map key symbols from SAM to Apple 1 map_key: cp &0c jr z,del_key cp &fc jr z,tab_key cp &80 jr nc,ignore_key cp &61 ; 'a' ret c cp &7b ; 'z'+1 ret nc and %11011111 ret del_key: ld a,&5f ; Delete -> _ ret tab_key: ld a,&09 ; standard tab ret invalid_key: ld a,&20 ; space for invalid ret ignore_key: xor a ; ignore ret ; Get a type-in key from the imported file get_filekey: ld a,file_page+rom0_off out (lmpr),a ld hl,(filepos) ld a,h or l or (hl) jr z,file_done ; jump if no file file_skip: ld a,(hl) ; fetch next file character inc hl and a jr z,clear_file2 ; clear file at end cp &0d ; CR? jr z,file_skip ; ignore CR in file cp &0a ; LF? jr nz,file_done ld a,&0d ; convert LF to CR file_done: ld (filepos),hl ex af,af' ld a,screen_page+rom0_off out (lmpr),a ex af,af' and a ret filepos: defw 0 ; Clear the file area to disable type-in clear_file: ld a,file_page+rom0_off out (lmpr),a clear_file2: ld hl,0 ld c,l clr_file_lp: ld (hl),c inc l jr nz,clr_file_lp inc h bit 7,h jr nz,clr_file_lp ld h,l xor a ; no file character jr file_done update_io: ld a,&f7 in a,(status) and %00100000 jr nz,not_esc ; jump if Esc not pressed still_esc: ld a,&f7 in a,(status) and %00100000 jr z,still_esc ; wait until Esc released call clear_file ; Esc cancels type-in mode ld a,&fe in a,(keyboard) rra ld a,&1b ; Esc character jr c,got_key ; unshifted gives chr ld a,&c9 ; RET ld (main_loop),a ; exit to reset at next instruction jr no_key not_esc: ld a,&7f in a,(keyboard) bit 1,a jr nz,not_sym ld a,&f7 in a,(keyboard) rra ld c,&ff ; line interrupt disable jr nc,got_line rra ld c,88 ; centre of display (312/2-68=88) jr c,not_sym got_line: ld a,c out (status),a ; set or disable line interrupt not_sym: ld hl,kbd_ctrl bit 7,(hl) ; key available? jr nz,no_key ; no need to read more yet call get_filekey ; got a type-in key from file? jr nz,got_key ; jump if we have skip_key: ld a,&1f+rom1_on out (lmpr),a call getkey ex af,af' ld a,screen_page+rom0_off out (lmpr),a ex af,af' jr z,no_key call map_key and a jr z,skip_key got_key: ld hl,kbd_data ld (hl),a set 7,(hl) ; bit 7 always set inc l set 7,(hl) ; key available no_key: ld hl,dsp_data bit 7,(hl) ; display char available? jr z,no_char res 7,(hl) ; display ready ld a,(hl) ; fetch character to display and a jr z,done_draw cp &7f jr z,done_draw push af ld a,&00 ; space call display_chr ; erase cursor pop af cp &5f ; backspace? (underscore) jr z,done_draw2 ; if so, no need to process further push af call map_chr ; map output from SAM->Apple 1 call display_chr ; show it pop af done_draw2: call advance_chr ; advance the cursor position ld a,&3f ; cursor block call display_chr ; show cursor done_draw: no_char: ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Interrupt handling setup_im2: ld hl,im2_table ld a,im2_jp/256 im2_fill: ld (hl),a inc l jr nz,im2_fill inc h ld (hl),a ; complete the final entry ld a,im2_table/256 ld i,a im 2 ; set interrupt mode 2 ret im2_handler: push af push bc push de push hl ex af,af' exx push af push bc push de push hl push ix push iy in a,(lmpr) push af ld a,screen_page+rom0_off out (lmpr),a call update_io pop af out (lmpr),a pop iy pop ix pop hl pop de pop bc pop af exx ex af,af' pop hl pop de pop bc pop af ei reti end_1: ; IM 2 table must be aligned to 256-byte boundary defs -$\256 im2_table: defs 257 ; IM 2 vector must have LSB==MSB defs $/256-1 stack_top: ; stack fits nicely in the slack space im2_jp: jp im2_handler ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 65C02 emulation execute: ld a,&1a ; LD A,(DE) ld (main_loop),a call load_state jr main_loop read_write_loop: write_loop: ld a,h cp &d0 jr z,io_write zwrite_loop: zread_write_loop: main_loop: ld a,(de) ; fetch opcode inc de ; PC=PC+1 ld l,a ld h,decode_table/256 ld a,(hl) ; handler low inc h ld h,(hl) ; handler high ld l,a jp (hl) ; execute! ; I/O write io_write: ld a,l cp &12 ; display char? jr nz,main_loop set 7,(hl) ; display busy jp main_loop read_loop: zread_loop: ld a,h cp &d0 jr z,io_read ld a,(de) ; fetch opcode inc de ; PC=PC+1 ld l,a ld h,decode_table/256 ld a,(hl) ; handler low inc h ld h,(hl) ; handler high ld l,a jp (hl) ; execute! io_read: ld a,l cp &10 ; key read? jr nz,main_loop inc l res 7,(hl) ; key not available jp main_loop ; 6502 addressing modes, shared by logical and arithmetic ; instructions, but inlined into the load and store. a_indirect_x: ld a,(de) ; indirect pre-indexed with X inc de defb &fd add a,h ; add X (may wrap in zero page) ld l,a ld h,0 ld a,(hl) inc hl ld h,(hl) ld l,a jp (ix) a_zero_page: ld a,(de) ; zero-page inc de ld l,a ld h,0 jp (ix) a_absolute: ex de,hl ; absolute (2-bytes) ld e,(hl) inc hl ld d,(hl) inc hl ex de,hl jp (ix) a_indirect_y: ld a,(de) ; indirect post-indexed with Y inc de ld l,a ld h,0 defb &fd ld a,l ; Y add a,(hl) inc l ; (may wrap in zero page) ld h,(hl) ld l,a ld a,0 adc a,h ld h,a jp (ix) a_zero_page_x: ld a,(de) ; zero-page indexed with X inc de defb &fd add a,h ; add X (may wrap in zero page) ld l,a ld h,0 jp (ix) a_zero_page_y: ld a,(de) ; zero-page indexed with Y inc de defb &fd add a,l ; add Y (may wrap in zero page) ld l,a ld h,0 jp (ix) a_absolute_y: ex de,hl ; absolute indexed with Y defb &fd ld a,l ; Y add a,(hl) ld e,a inc hl ld a,0 adc a,(hl) ld d,a inc hl ex de,hl jp (ix) a_absolute_x: ex de,hl ; absolute indexed with X defb &fd ld a,h ; X add a,(hl) ld e,a inc hl ld a,0 adc a,(hl) ld d,a inc hl ex de,hl jp (ix) a_indirect_z: ld a,(de) ; indirect zero-page [65C02] inc de ld l,a ld h,0 ld a,(hl) inc l ; (may wrap in zero page) ld h,(hl) ld l,a ld a,(hl) inc hl ld h,(hl) ld l,a jp (ix) ; Instruction implementations i_nop: equ main_loop i_undoc_1: equ main_loop i_undoc_3: inc de ; 3-byte NOP i_undoc_2: inc de ; 2-byte NOP jp main_loop i_clc: exx ; clear carry ld c,0 exx jp main_loop i_sec: exx ; set carry ld c,1 exx jp main_loop i_cli: exx ; clear interrupt disable res 2,d exx jp main_loop i_sei: exx ; set interrupt disable set 2,d exx jp main_loop i_clv: exx ; clear overflow ld b,0 exx jp main_loop i_cld: exx ; clear decimal mode res 3,d exx xor a ; NOP ld (adc_daa),a ; use binary mode for adc ld (sbc_daa),a ; use binary mode for sbc jp main_loop i_sed: exx set 3,d exx ld a,&27 ; DAA ld (adc_daa),a ; use decimal mode for adc ld (sbc_daa),a ; use decimal mode for sbc jp main_loop i_bpl: ld a,(de) inc de ex af,af' ld l,a ; copy N ex af,af' bit 7,l ; test N jr z,i_branch ; branch if plus jp main_loop i_bmi: ld a,(de) inc de ex af,af' ld l,a ; copy N ex af,af' bit 7,l ; test N jr nz,i_branch ; branch if minus jp main_loop i_bvc: ld a,(de) ; V in bit 6 inc de ; V set if non-zero exx bit 6,b exx jr z,i_branch ; branch if V clear jp main_loop i_bvs: ld a,(de) ; V in bit 6 inc de exx bit 6,b exx jr nz,i_branch ; branch if V set jp main_loop i_bcc: ld a,(de) ; C in bit 1 inc de exx bit 0,c exx jr z,i_branch ; branch if C clear jp main_loop i_bcs: ld a,(de) inc de exx bit 0,c exx jr nz,i_branch ; branch if C set jp main_loop i_beq: ld a,(de) inc de inc c dec c ; zero? jr z,i_branch ; branch if zero jp main_loop i_bne: ld a,(de) inc de inc c dec c ; zero? jp z,main_loop ; no branch if not zero i_branch: ld l,a ; offset low rla ; set carry with sign sbc a,a ; form high byte for offset ld h,a add hl,de ; PC=PC+e ex de,hl jp main_loop i_bra: ld a,(de) ; unconditional branch [65C02] inc de jr i_branch i_bbr_0: ld a,%00000001 ; BBRn [65C02] jp i_bbs i_bbr_1: ld a,%00000010 jp i_bbs i_bbr_2: ld a,%00000100 jp i_bbs i_bbr_3: ld a,%00001000 jp i_bbs i_bbr_4: ld a,%00010000 jp i_bbs i_bbr_5: ld a,%00100000 jp i_bbs i_bbr_6: ld a,%01000000 jp i_bbs i_bbr_7: ld a,%10000000 i_bbr: ex de,hl ld e,(hl) inc hl ld d,0 ex de,hl and (hl) ld a,(de) inc de jr z,i_branch ; ToDo: read_loop after branch jp read_loop i_bbs_0: ld a,%00000001 ; BBSn [65C02] jp i_bbs i_bbs_1: ld a,%00000010 jp i_bbs i_bbs_2: ld a,%00000100 jp i_bbs i_bbs_3: ld a,%00001000 jp i_bbs i_bbs_4: ld a,%00010000 jp i_bbs i_bbs_5: ld a,%00100000 jp i_bbs i_bbs_6: ld a,%01000000 jp i_bbs i_bbs_7: ld a,%10000000 i_bbs: ex de,hl ld e,(hl) inc hl ld d,0 ex de,hl and (hl) ld a,(de) inc de jr nz,i_branch ; ToDo: read_loop after branch jp read_loop i_jmp_a: ex de,hl ; JMP nn ld e,(hl) inc hl ld d,(hl) inc hl jp main_loop i_jmp_i: ex de,hl ; JMP (nn) ld e,(hl) inc hl ld d,(hl) inc hl ex de,hl ld e,(hl) ; inc l ; 6502 bug wraps within page, *OR* inc hl ; 65C02 spans pages correctly ld d,(hl) jp main_loop i_jmp_ax: ex de,hl ; JMP (nn,X) [65C02] defb &fd ld a,h ; X add a,(hl) ld e,a inc hl ld a,0 adc a,(hl) ; carry spans page ld d,a inc hl ex de,hl ld e,(hl) inc hl ld d,(hl) jp main_loop i_jsr: ex de,hl ; JSR nn ld e,(hl) ; subroutine low inc hl ; only 1 inc - we push ret-1 ld d,(hl) ; subroutine high ld a,h ; PCh exx ld (hl),a ; push ret-1 high byte dec l ; S-- exx ld a,l ; PCl exx ld (hl),a ; push ret-1 low byte dec l ; S-- exx jp main_loop i_brk: inc de ; return to BRK+2 ld a,d exx ld (hl),a ; push return MSB dec l ; S-- exx ld a,e exx ld (hl),a ; push return LSB dec l ; S-- ld a,d or %00010000 ; set B flag (temp) ld (hl),a ; push flags with B set dec l ; S-- set 2,d ; set I flag exx ld de,(m6502_int) ; fetch interrupt handler jp main_loop i_rts: exx ; RTS inc l ; S++ ld a,(hl) ; PC LSB exx ld e,a exx inc l ; S++ ld a,(hl) ; PC MSB exx ld d,a inc de ; PC++ (strange but true) jp main_loop i_rti: exx ; RTI inc l ; S++ ld a,(hl) ; pop P or %00110000 ; set T and B flags call split_p_exx ; split P into status+flags (already exx) exx inc l ; S++ ld a,(hl) ; pop return LSB exx ld e,a exx inc l ; S++ ld a,(hl) ; pop return MSB exx ld d,a jp main_loop i_php: call make_p ; make P from status+flags or %00010000 ; B always pushed as 1 exx ld (hl),a dec l ; S-- exx jp main_loop i_plp: exx ; PLP inc l ; S++ ld a,(hl) ; P or %00110000 ; set T and B flags exx call split_p ; split P into status+flags jp main_loop i_pha: ld a,b ; PHA exx ld (hl),a dec l ; S-- exx jp main_loop i_pla: exx ; PLA inc l ; S++ ld a,(hl) exx ld b,a ; set A ld c,b ; set Z ex af,af' ; set N jp main_loop i_phx: defb &fd ; PHX [65C02] ld a,h ; X exx ld (hl),a dec l ; S-- exx jp main_loop i_plx: exx ; PLX [65C02] inc l ; S++ ld a,(hl) exx defb &fd ld h,a ; set X ld c,a ; set Z ex af,af' ; set N jp main_loop i_phy: defb &fd ; PHY [65C02] ld a,l ; Y exx ld (hl),a dec l ; S-- exx jp main_loop i_ply: exx ; PLY [65C02] inc l ; S++ ld a,(hl) exx defb &fd ld l,a ; set Y ld c,a ; set Z ex af,af' ; set N jp main_loop i_dex: defb &fd ; DEX dec h ; X-- defb &fd ld a,h ; X ld c,a ; set Z ex af,af' ; set N jp main_loop i_dey: defb &fd ; DEY dec l ; Y-- defb &fd ld a,l ; Y ld c,a ; set Z ex af,af' ; set N jp main_loop i_inx: defb &fd ; INX inc h ; X++ defb &fd ld a,h ; X ld c,a ; set Z ex af,af' ; set N jp main_loop i_iny: defb &fd ; INY inc l ; Y++ defb &fd ld a,l ; Y ld c,a ; set Z ex af,af' ; set N jp main_loop i_txa: defb &fd ; TXA ld a,h ; X ld b,a ; A=X ld c,b ; set Z ex af,af' ; set N jp main_loop i_tya: defb &fd ; TYA ld a,l ; Y ld b,a ; A=Y ld c,b ; set Z ex af,af' ; set N jp main_loop i_tax: defb &fd ; TAX ld h,b ; X=A ld c,b ; set Z ld a,b ex af,af' ; set N jp main_loop i_tay: defb &fd ; TAY ld l,b ; Y=A ld c,b ; set Z ld a,b ex af,af' ; set N jp main_loop i_txs: defb &fd ; TXS ld a,h ; X exx ld l,a ; set S (no flags set) exx jp main_loop i_tsx: exx ; TSX ld a,l ; fetch S exx defb &fd ld h,a ; X=S ld c,a ; set Z ex af,af' ; set N jp main_loop ; For speed, LDA/LDX/LDY instructions have addressing inlined i_lda_ix: ld a,(de) ; LDA ($nn,X) inc de defb &fd add a,h ; add X (may wrap in zero page) ld l,a ld h,0 ld a,(hl) inc hl ld h,(hl) ld l,a ld b,(hl) ; set A ld c,b ; set Z ld a,b ex af,af' ; set N jp zread_loop i_lda_z: ld a,(de) ; LDA $nn inc de ld l,a ld h,0 ld b,(hl) ; set A ld c,b ; set Z ld a,b ex af,af' ; set N jp zread_loop i_lda_a: ex de,hl ; LDA $nnnn ld e,(hl) inc hl ld d,(hl) inc hl ex de,hl ld b,(hl) ; set A ld c,b ; set Z ld a,b ex af,af' ; set N jp read_loop i_lda_iy: ld a,(de) ; LDA ($nn),Y inc de ld l,a ld h,0 defb &fd ld a,l ; Y add a,(hl) inc l ; (may wrap in zero page) ld h,(hl) ld l,a ld a,0 adc a,h ld h,a ld b,(hl) ; set A ld c,b ; set Z ld a,b ex af,af' ; set N jp read_loop i_lda_zx: ld a,(de) ; LDA $nn,X inc de defb &fd add a,h ; add X (may wrap in zero page) ld l,a ld h,0 ld b,(hl) ; set A ld c,b ; set Z ld a,b ex af,af' ; set N jp zread_loop i_lda_ay: ex de,hl ; LDA $nnnn,Y defb &fd ld a,l ; Y add a,(hl) ld e,a inc hl ld a,0 adc a,(hl) ld d,a inc hl ex de,hl ld b,(hl) ; set A ld c,b ; set Z ld a,b ex af,af' ; set N jp read_loop i_lda_ax: ex de,hl ; LDA $nnnn,X defb &fd ld a,h ; X add a,(hl) ld e,a inc hl ld a,0 adc a,(hl) ld d,a inc hl ex de,hl ld b,(hl) ; set A ld c,b ; set Z ld a,b ex af,af' ; set N jp read_loop i_lda_i: ld a,(de) ; LDA #$nn inc de ld b,a ; set A ld c,b ; set Z ex af,af' ; set N jp main_loop i_lda_iz: ld a,(de) ; LDA ($nn) [65C02] inc de ld l,a ld h,0 ld a,(hl) inc l ; (may wrap in zero page) ld h,(hl) ld l,a ld a,(hl) inc hl ld h,(hl) ld l,a ld b,(hl) ; set A ld c,b ; set Z ld a,b ex af,af' ; set N jp read_loop i_ldx_z: ld a,(de) ; LDX $nn inc de ld l,a ld h,0 ld a,(hl) defb &fd ld h,a ; set X ld c,a ; set Z ex af,af' ; set N jp zread_loop i_ldx_a: ex de,hl ; LDX $nnnn ld e,(hl) inc hl ld d,(hl) inc hl ex de,hl ld a,(hl) defb &fd ld h,a ; set X ld c,a ; set Z ex af,af' ; set N jp read_loop i_ldx_zy: ld a,(de) ; LDX $nn,Y inc de defb &fd add a,l ; add Y (may wrap in zero page) ld l,a ld h,0 ld a,(hl) defb &fd ld h,a ; set X ld c,a ; set Z ex af,af' ; set N jp zread_loop i_ldx_ay: ex de,hl ; LDX $nnnn,Y defb &fd ld a,l ; Y add a,(hl) ld e,a inc hl ld a,0 adc a,(hl) ld d,a inc hl ex de,hl ld a,(hl) defb &fd ld h,a ; set X ld c,a ; set Z ex af,af' ; set N jp read_loop i_ldx_i: ld a,(de) ; LDX #$nn inc de defb &fd ld h,a ; set X ld c,a ; set Z ex af,af' ; set N jp main_loop i_ldy_z: ld a,(de) ; LDY $nn inc de ld l,a ld h,0 ld a,(hl) defb &fd ld l,a ; set Y ld c,a ; set Z ex af,af' ; set N jp zread_loop i_ldy_a: ex de,hl ; LDY $nnnn ld e,(hl) inc hl ld d,(hl) inc hl ex de,hl ld a,(hl) defb &fd ld l,a ; set Y ld c,a ; set Z ex af,af' ; set N jp read_loop i_ldy_zx: ld a,(de) ; LDY $nn,X inc de defb &fd add a,h ; add X (may wrap in zero page) ld l,a ld h,0 ld a,(hl) defb &fd ld l,a ; set Y ld c,a ; set Z ex af,af' ; set N jp zread_loop i_ldy_ax: ex de,hl ; LDY $nnnn,X defb &fd ld a,h ; X add a,(hl) ld e,a inc hl ld a,0 adc a,(hl) ld d,a inc hl ex de,hl ld a,(hl) defb &fd ld l,a ; set Y ld c,a ; set Z ex af,af' ; set N jp read_loop i_ldy_i: ld a,(de) ; LDY #$nn inc de defb &fd ld l,a ; set Y ld c,a ; set Z ex af,af' ; set N jp main_loop ; For speed, STA/STX/STY instructions have addressing inlined i_sta_ix: ld a,(de) ; STA ($xx,X) inc de defb &fd add a,h ; add X (may wrap in zero page) ld l,a ld h,0 ld a,(hl) inc hl ld h,(hl) ld l,a ld (hl),b jp zwrite_loop i_sta_z: ld a,(de) ; STA $nn inc de ld l,a ld h,0 ld (hl),b jp zwrite_loop i_sta_iy: ld a,(de) inc de ld l,a ld h,0 defb &fd ld a,l ; Y add a,(hl) inc l ld h,(hl) ld l,a ld a,0 adc a,h ld h,a ld (hl),b jp write_loop i_sta_zx: ld a,(de) inc de defb &fd add a,h ; add X (may wrap in zero page) ld l,a ld h,0 ld (hl),b jp zwrite_loop i_sta_ay: ex de,hl defb &fd ld a,l ; Y add a,(hl) ld e,a inc hl ld a,0 adc a,(hl) ld d,a inc hl ex de,hl ld (hl),b jp write_loop i_sta_ax: ex de,hl defb &fd ld a,h ; X add a,(hl) ld e,a inc hl ld a,0 adc a,(hl) ld d,a inc hl ex de,hl ld (hl),b jp write_loop i_sta_a: ex de,hl ld e,(hl) inc hl ld d,(hl) inc hl ex de,hl ld (hl),b jp write_loop i_sta_iz: ld a,(de) ; STA ($nn) [65C02] inc de ld l,a ld h,0 ld a,(hl) inc l ; (may wrap in zero page) ld h,(hl) ld l,a ld a,(hl) inc hl ld h,(hl) ld l,a ld (hl),b ; store A jp write_loop i_stx_z: ld a,(de) inc de ld l,a ld h,0 defb &fd ld a,h ; X ld (hl),a jp zwrite_loop i_stx_zy: ld a,(de) inc de defb &fd add a,l ; add Y (may wrap in zero page) ld l,a ld h,0 defb &fd ld a,h ; X ld (hl),a jp zwrite_loop i_stx_a: ex de,hl ld e,(hl) inc hl ld d,(hl) inc hl ex de,hl defb &fd ld a,h ; X ld (hl),a jp write_loop i_sty_z: ld a,(de) inc de ld l,a ld h,0 defb &fd ld a,l ; Y ld (hl),a jp zwrite_loop i_sty_zx: ld a,(de) inc de defb &fd add a,h ; add X (may wrap in zero page) ld l,a ld h,0 defb &fd ld a,l ; Y ld (hl),a jp zwrite_loop i_sty_a: ex de,hl ld e,(hl) inc hl ld d,(hl) inc hl ex de,hl defb &fd ld a,l ; Y ld (hl),a jp write_loop i_stz_z: ld a,(de) ; STZ $nn [65C02] inc de ld l,a ld h,0 ld (hl),h jp zwrite_loop i_stz_zx: ld a,(de) inc de defb &fd add a,h ; add X (may wrap in zero page) ld l,a ld h,0 ld (hl),h jp zwrite_loop i_stz_ax: ex de,hl defb &fd ld a,h ; X add a,(hl) ld e,a inc hl ld a,0 adc a,(hl) ld d,a inc hl ex de,hl ld (hl),0 jp write_loop i_stz_a: ex de,hl ld e,(hl) inc hl ld d,(hl) inc hl ex de,hl ld (hl),0 jp write_loop i_adc_ix: ld ix,i_adc jp a_indirect_x i_adc_z: ld ix,i_adc jp a_zero_page i_adc_a: ld ix,i_adc jp a_absolute i_adc_zx: ld ix,i_adc jp a_zero_page_x i_adc_ay: ld ix,i_adc jp a_absolute_y i_adc_ax: ld ix,i_adc jp a_absolute_x i_adc_iy: ld ix,i_adc jp a_indirect_y i_adc_iz: ld ix,i_adc ; [65C02] jp a_indirect_z i_adc_i: ld h,d ld l,e inc de i_adc: exx ld a,c ; C exx rra ; set up carry ld a,b ; A adc a,(hl) ; A+M+C adc_daa: nop ld b,a ; set A ; jp set_nvzc ; fall through to set_nvzc... set_nvzc: ld c,a ; set Z rla ; C in bit 0, no effect on V exx ld c,a ; set C jp pe,set_v ld b,%00000000 ; V clear exx ld a,c ex af,af' ; set N jp read_loop set_v: ld b,%01000000 ; V set exx ld a,c ex af,af' ; set N jp read_loop i_sbc_ix: ld ix,i_sbc jp a_indirect_x i_sbc_z: ld ix,i_sbc jp a_zero_page i_sbc_a: ld ix,i_sbc jp a_absolute i_sbc_zx: ld ix,i_sbc jp a_zero_page_x i_sbc_ay: ld ix,i_sbc jp a_absolute_y i_sbc_ax: ld ix,i_sbc jp a_absolute_x i_sbc_iy: ld ix,i_sbc jp a_indirect_y i_sbc_iz: ld ix,i_sbc ; [65C02] jp a_indirect_z i_sbc_i: ld h,d ld l,e inc de i_sbc: exx ld a,c ; C exx rra ; set up carry ld a,b ; A ccf ; uses inverted carry sbc a,(hl) ; A-M-(1-C) sbc_daa: nop ccf ; no carry for overflow ld b,a ; set A jp set_nvzc i_and_ix: ld ix,i_and jp a_indirect_x i_and_z: ld ix,i_and jp a_zero_page i_and_a: ld ix,i_and jp a_absolute i_and_zx: ld ix,i_and jp a_zero_page_x i_and_ay: ld ix,i_and jp a_absolute_y i_and_ax: ld ix,i_and jp a_absolute_x i_and_iy: ld ix,i_and jp a_indirect_y i_and_iz: ld ix,i_and ; [65C02] jp a_indirect_z i_and_i: ld h,d ld l,e inc de i_and: ld a,b ; A and (hl) ; A&x ld b,a ; set A ld c,b ; set Z ex af,af' ; set N jp read_loop i_eor_ix: ld ix,i_eor jp a_indirect_x i_eor_z: ld ix,i_eor jp a_zero_page i_eor_a: ld ix,i_eor jp a_absolute i_eor_zx: ld ix,i_eor jp a_zero_page_x i_eor_ay: ld ix,i_eor jp a_absolute_y i_eor_ax: ld ix,i_eor jp a_absolute_x i_eor_iy: ld ix,i_eor jp a_indirect_y i_eor_iz: ld ix,i_eor ; [65C02] jp a_indirect_z i_eor_i: ld h,d ld l,e inc de i_eor: ld a,b ; A xor (hl) ; A^x ld b,a ; set A ld c,b ; set Z ex af,af' ; set N jp read_loop i_ora_ix: ld ix,i_ora jp a_indirect_x i_ora_z: ld ix,i_ora jp a_zero_page i_ora_a: ld ix,i_ora jp a_absolute i_ora_zx: ld ix,i_ora jp a_zero_page_x i_ora_ay: ld ix,i_ora jp a_absolute_y i_ora_ax: ld ix,i_ora jp a_absolute_x i_ora_iy: ld ix,i_ora jp a_indirect_y i_ora_iz: ld ix,i_ora ; [65C02] jp a_indirect_z i_ora_i: ld h,d ld l,e inc de i_ora: ld a,b ; A or (hl) ; A|x ld b,a ; set A ld c,b ; set Z ex af,af' ; set N jp read_loop i_cmp_ix: ld ix,i_cmp jp a_indirect_x i_cmp_z: ld ix,i_cmp jp a_zero_page i_cmp_a: ld ix,i_cmp jp a_absolute i_cmp_zx: ld ix,i_cmp jp a_zero_page_x i_cmp_ay: ld ix,i_cmp jp a_absolute_y i_cmp_ax: ld ix,i_cmp jp a_absolute_x i_cmp_iy: ld ix,i_cmp jp a_indirect_y i_cmp_iz: ld ix,i_cmp ; [65C02] jp a_indirect_z i_cmp_i: ld h,d ld l,e inc de i_cmp: ld a,b ; A sub (hl) ; A-x (result discarded) ccf exx rl c ; retrieve carry exx ld c,a ; set Z ex af,af' ; set N jp read_loop i_cpx_z: ld ix,i_cpx jp a_zero_page i_cpx_a: ld ix,i_cpx jp a_absolute i_cpx_i: ld h,d ld l,e inc de i_cpx: defb &fd ld a,h ; X sub (hl) ; X-x (result discarded) ccf exx rl c ; retrieve carry exx ld c,a ; set Z ex af,af' ; set N jp read_loop i_cpy_z: ld ix,i_cpy jp a_zero_page i_cpy_a: ld ix,i_cpy jp a_absolute i_cpy_i: ld h,d ld l,e inc de i_cpy: defb &fd ld a,l ; Y sub (hl) ; Y-x (result discarded) ccf exx rl c ; retrieve carry exx ld c,a ; set Z ex af,af' ; set N jp read_loop i_dec_z: ld ix,i_dec_zp jp a_zero_page i_dec_zx: ld ix,i_dec_zp jp a_zero_page_x i_dec_a: ld ix,i_dec jp a_absolute i_dec_ax: ld ix,i_dec jp a_absolute_x i_dec: dec (hl) ; mem-- ld c,(hl) ; set Z ld a,c ex af,af' ; set N jp read_write_loop i_dec_zp: dec (hl) ; zero-page-- ld c,(hl) ; set Z ld a,c ex af,af' ; set N jp zread_write_loop i_dec_ac: dec b ; A-- [65C02] ld c,b ; set Z ld a,b ex af,af' ; set N jp main_loop i_inc_z: ld ix,i_inc_zp jp a_zero_page i_inc_zx: ld ix,i_inc_zp jp a_zero_page_x i_inc_a: ld ix,i_inc jp a_absolute i_inc_ax: ld ix,i_inc jp a_absolute_x i_inc: inc (hl) ; mem++ ld c,(hl) ; set Z ld a,c ex af,af' ; set N jp read_write_loop i_inc_zp: inc (hl) ; zero-page++ ld c,(hl) ; set Z ld a,c ex af,af' ; set N jp zread_write_loop i_inc_ac: inc b ; A++ [65C02] ld c,b ; set Z ld a,b ex af,af' ; set N jp main_loop i_asl_z: ld ix,i_asl jp a_zero_page i_asl_zx: ld ix,i_asl jp a_zero_page_x i_asl_a: ld ix,i_asl jp a_absolute i_asl_ax: ld ix,i_asl jp a_absolute_x i_asl_acc: sla b ; A << 1 exx rl c ; retrieve carry exx ld c,b ; set Z ld a,b ex af,af' ; set N jp main_loop i_asl: ld a,(hl) ; x add a,a ; x << 1 ld (hl),a ; set memory exx rl c ; retrieve carry exx ld c,a ; set Z ex af,af' ; set N jp write_loop i_lsr_z: ld ix,i_lsr jp a_zero_page i_lsr_zx: ld ix,i_lsr jp a_zero_page_x i_lsr_a: ld ix,i_lsr jp a_absolute i_lsr_ax: ld ix,i_lsr jp a_absolute_x i_lsr_acc: srl b ; A >> 1 exx rl c ; retrieve carry exx ld c,b ; set Z ld a,b ex af,af' ; set N jp main_loop i_lsr: ld a,(hl) ; x srl a ; x >> 1 ld (hl),a ; set memory exx rl c ; retrieve carry exx ld c,a ; set Z ex af,af' ; set N jp write_loop i_rol_z: ld ix,i_rol jp a_zero_page i_rol_zx: ld ix,i_rol jp a_zero_page_x i_rol_a: ld ix,i_rol jp a_absolute i_rol_ax: ld ix,i_rol jp a_absolute_x i_rol_acc: ld a,b exx rr c ; set up carry rla ; A << 1 rl c ; retrieve carry exx ld b,a ; set A ld c,b ; set Z ex af,af' ; set N jp main_loop i_rol: ld a,(hl) ; x exx rr c ; set up carry rla ; x << 1 rl c ; retrieve carry exx ld (hl),a ; set memory ld c,a ; set Z ex af,af' ; set N jp write_loop i_ror_z: ld ix,i_ror jp a_zero_page i_ror_zx: ld ix,i_ror jp a_zero_page_x i_ror_a: ld ix,i_ror jp a_absolute i_ror_ax: ld ix,i_ror jp a_absolute_x i_ror_acc: ld a,b exx rr c ; set up carry rra ; A >> 1 rl c ; retrieve carry exx ld b,a ; set A ld c,b ; set Z ex af,af' ; set N jp main_loop i_ror: ld a,(hl) ; x exx rr c ; set up carry rra ; x >> 1 rl c ; retrieve carry exx ld (hl),a ; set memory ld c,a ; set Z ex af,af' ; set N jp write_loop i_bit_z: ld ix,i_bit jp a_zero_page i_bit_zx: ld ix,i_bit jp a_zero_page_x i_bit_a: ld ix,i_bit jp a_absolute i_bit_ax: ld ix,i_bit jp a_absolute_x i_bit_i: ld h,d ; BIT #$nn ld l,e inc de i_bit: ld c,(hl) ; x ld a,c ex af,af' ; set N ld a,c and %01000000 ; V flag set from bit 6 exx ld b,a ; set V exx ld a,b ; A and c ; perform BIT test ld c,a ; set Z jp read_loop i_tsb_z: ld ix,i_tsb ; TSB [65C02] jp a_zero_page i_tsb_a: ld ix,i_tsb jp a_absolute i_tsb: ld c,(hl) ; x ld a,c or b ; set bits from A ld (hl),a ld a,c and b ; test bits against A ld c,a ; set Z jp write_loop i_trb_z: ld ix,i_trb ; TRB [65C02] jp a_zero_page i_trb_a: ld ix,i_trb jp a_absolute i_trb: ld c,(hl) ; x ld a,b ; A cpl ; ~A and c ; reset bits from A ld (hl),a ld a,c and b ; test bits against A ld c,a ; set Z jp write_loop i_smb_0: ld a,%00000001 ; SMBn [65C02] jp i_smb i_smb_1: ld a,%00000010 jp i_smb i_smb_2: ld a,%00000100 jp i_smb i_smb_3: ld a,%00001000 jp i_smb i_smb_4: ld a,%00010000 jp i_smb i_smb_5: ld a,%00100000 jp i_smb i_smb_6: ld a,%01000000 jp i_smb i_smb_7: ld a,%10000000 i_smb: ex de,hl ld e,(hl) inc hl ld d,0 ex de,hl or (hl) ld (hl),a jp zwrite_loop i_rmb_0: ld a,%11111110 ; RMBn [65C02] jp i_smb i_rmb_1: ld a,%11111101 jp i_smb i_rmb_2: ld a,%11111011 jp i_smb i_rmb_3: ld a,%11110111 jp i_smb i_rmb_4: ld a,%11101111 jp i_smb i_rmb_5: ld a,%11011111 jp i_smb i_rmb_6: ld a,%10111111 jp i_smb i_rmb_7: ld a,%01111111 i_rmb: ex de,hl ld e,(hl) inc hl ld d,0 ex de,hl and (hl) ld (hl),a jp zwrite_loop i_stp: dec de ; STP [65C02] ld a,bord_stp out (border),a jp main_loop i_wai: dec de ; WAI [65C02] ld a,bord_wai out (border),a jp main_loop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; make_p: ex af,af' and %10000000 ; keep N ld l,a ; N ex af,af' ld a,c ; Z sub 1 ; set carry if zero rla rla and %00000010 ; keep 6510 Z bit or l ; N+Z exx or b ; N+V+Z ld e,a ld a,c and %00000001 ; keep C or e ; N+V+Z+C exx ret split_p: exx split_p_exx: ld e,a ; save P and %00111100 ; keep CPU bits ld d,a ; set status ld a,e ex af,af' ; set N ld a,e and %01000000 ; keep V ld b,a ; set V ld a,e and %00000001 ; keep C ld c,a ; set C ld a,e cpl and %00000010 ; Z=0 NZ=2 exx ld c,a ; set NZ ret load_state: ld a,(reg_a) ld b,a ; set A ld a,(reg_x) defb &fd ld h,a ; set X to IYh ld a,(reg_y) defb &fd ld l,a ; set Y to IYl exx ld a,(reg_s) ld l,a ; set S ld h,&01 ; MSB for stack pointer exx ld a,(reg_p) call split_p ; set P and flags ld de,(reg_pc) ; set PC ret save_state: ld a,b ; get A ld (reg_a),a defb &fd ld a,h ; get X from IYh ld (reg_x),a defb &fd ld a,l ; get Y from IYl ld (reg_y),a exx ld a,l ; get S ld (reg_s),a exx call make_p ; get P ld (reg_pc),de ret ; While running we have the 6502 registers in Z80 registers ; These are used only to hold the state before/afterwards reg_a: defb 0 reg_p: defb 0 reg_x: defb 0 reg_y: defb 0 reg_s: defb 0 reg_pc: defw 0 ; Reordering the decode table to group low and high bytes means ; we avoid any 16-bit arithmetic for the decode stage, saving ; 12T on the old method (cool tip from Dave Laundon) reorder_256: equ im2_table reorder_decode:ld hl,decode_table ld d,h ld e,l ld bc,reorder_256 ; 256-byte temporary store reorder_lp: ld a,(hl) ; low byte ld (de),a inc l inc e ld a,(hl) ; high byte ld (bc),a inc hl inc c jr nz,reorder_lp dec h ; back to 2nd half (high bytes) reorder_lp2: ld a,(bc) ld (hl),a inc c inc l jr nz,reorder_lp2 ld a,&c9 ; RET ld (reorder_decode),A ret defs -$\256 ; align table to 256-byte boundary decode_table: DEFW i_brk,i_ora_ix,i_undoc_1,i_undoc_2 ; 00 DEFW i_tsb_z,i_ora_z,i_asl_z,i_rmb_0 ; 04 DEFW i_php,i_ora_i,i_asl_acc,i_undoc_2 ; 08 DEFW i_tsb_a,i_ora_a,i_asl_a,i_bbr_0 ; 0C DEFW i_bpl,i_ora_iy,i_ora_iz,i_undoc_2 ; 10 DEFW i_trb_z,i_ora_zx,i_asl_zx,i_rmb_1 ; 14 DEFW i_clc,i_ora_ay,i_inc_ac,i_undoc_3 ; 18 DEFW i_trb_a,i_ora_ax,i_asl_ax,i_bbr_1 ; 1C DEFW i_jsr,i_and_ix,i_undoc_1,i_undoc_2 ; 20 DEFW i_bit_z,i_and_z,i_rol_z,i_rmb_2 ; 24 DEFW i_plp,i_and_i,i_rol_acc,i_undoc_2 ; 28 DEFW i_bit_a,i_and_a,i_rol_a,i_bbr_2 ; 2C DEFW i_bmi,i_and_iy,i_and_iz,i_undoc_2 ; 30 DEFW i_bit_zx,i_and_zx,i_rol_zx,i_rmb_3 ; 34 DEFW i_sec,i_and_ay,i_dec_ac,i_undoc_3 ; 38 DEFW i_bit_ax,i_and_ax,i_rol_ax,i_bbr_3 ; 3C DEFW i_rti,i_eor_ix,i_undoc_1,i_undoc_2 ; 40 DEFW i_undoc_2,i_eor_z,i_lsr_z,i_rmb_4 ; 44 DEFW i_pha,i_eor_i,i_lsr_acc,i_undoc_2 ; 48 DEFW i_jmp_a,i_eor_a,i_lsr_a,i_bbr_4 ; 4C DEFW i_bvc,i_eor_iy,i_eor_iz,i_undoc_2 ; 50 DEFW i_undoc_2,i_eor_zx,i_lsr_zx,i_rmb_5 ; 54 DEFW i_cli,i_eor_ay,i_phy,i_undoc_3 ; 58 DEFW i_undoc_3,i_eor_ax,i_lsr_ax,i_bbr_5 ; 5C DEFW i_rts,i_adc_ix,i_undoc_1,i_undoc_2 ; 60 DEFW i_stz_z,i_adc_z,i_ror_z,i_rmb_6 ; 64 DEFW i_pla,i_adc_i,i_ror_acc,i_undoc_2 ; 68 DEFW i_jmp_i,i_adc_a,i_ror_a,i_bbr_6 ; 6C DEFW i_bvs,i_adc_iy,i_adc_iz,i_undoc_2 ; 70 DEFW i_stz_zx,i_adc_zx,i_ror_zx,i_rmb_7 ; 74 DEFW i_sei,i_adc_ay,i_ply,i_undoc_3 ; 78 DEFW i_jmp_ax,i_adc_ax,i_ror_ax,i_bbr_7 ; 7C DEFW i_bra,i_sta_ix,i_undoc_2,i_undoc_2 ; 80 DEFW i_sty_z,i_sta_z,i_stx_z,i_smb_0 ; 84 DEFW i_dey,i_bit_i,i_txa,i_undoc_2 ; 88 DEFW i_sty_a,i_sta_a,i_stx_a,i_bbs_0 ; 8C DEFW i_bcc,i_sta_iy,i_sta_iz,i_undoc_2 ; 90 DEFW i_sty_zx,i_sta_zx,i_stx_zy,i_smb_1 ; 94 DEFW i_tya,i_sta_ay,i_txs,i_undoc_2 ; 98 DEFW i_stz_a,i_sta_ax,i_stz_ax,i_bbs_1 ; 9C DEFW i_ldy_i,i_lda_ix,i_ldx_i,i_undoc_2 ; A0 DEFW i_ldy_z,i_lda_z,i_ldx_z,i_smb_2 ; A4 DEFW i_tay,i_lda_i,i_tax,i_undoc_2 ; A8 DEFW i_ldy_a,i_lda_a,i_ldx_a,i_bbs_2 ; AC DEFW i_bcs,i_lda_iy,i_lda_iz,i_undoc_2 ; B0 DEFW i_ldy_zx,i_lda_zx,i_ldx_zy,i_smb_3 ; B4 DEFW i_clv,i_lda_ay,i_tsx,i_undoc_3 ; B8 DEFW i_ldy_ax,i_lda_ax,i_ldx_ay,i_bbs_3 ; BC DEFW i_cpy_i,i_cmp_ix,i_undoc_2,i_undoc_2 ; C0 DEFW i_cpy_z,i_cmp_z,i_dec_z,i_smb_4 ; C4 DEFW i_iny,i_cmp_i,i_dex,i_wai ; C8 DEFW i_cpy_a,i_cmp_a,i_dec_a,i_bbs_4 ; CC DEFW i_bne,i_cmp_iy,i_cmp_iz,i_undoc_2 ; D0 DEFW i_undoc_2,i_cmp_zx,i_dec_zx,i_smb_5 ; D4 DEFW i_cld,i_cmp_ay,i_phx,i_stp ; D8 DEFW i_undoc_3,i_cmp_ax,i_dec_ax,i_bbs_5 ; DC DEFW i_cpx_i,i_sbc_ix,i_undoc_2,i_undoc_2 ; E0 DEFW i_cpx_z,i_sbc_z,i_inc_z,i_smb_6 ; E4 DEFW i_inx,i_sbc_i,i_nop,i_undoc_2 ; E8 DEFW i_cpx_a,i_sbc_a,i_inc_a,i_bbs_6 ; EC DEFW i_beq,i_sbc_iy,i_sbc_iz,i_undoc_2 ; F0 DEFW i_undoc_2,i_sbc_zx,i_inc_zx,i_smb_7 ; F4 DEFW i_sed,i_sbc_ay,i_plx,i_undoc_3 ; F8 DEFW i_undoc_3,i_sbc_ax,i_inc_ax,i_bbs_7 ; FC font_data: MDAT "font.bin" mask_data: defb %00000011,%11111111 defb %11000000,%11111111 defb %11110000,%00111111 defb %11111100,%00001111 end: equ $ length: equ end-start ; Ken Wessen's custom BASIC+Krusader+Monitor ROM (&e000-&ffff) ; BRK handler points to mini-monitor in this version dump &e000 MDAT "65C02.rom.bin" ; Original Monitor ROM (&ff00-&ffff) ; If uncommented, this will replace the monitor ROM section from above dump &ff00 MDAT "apple1.rom"