; Chip8 emulation library (chiplib) v0.9B ; (c) 1998 Noah Stein ; Do not distribute versions modified in any way. include "tios.h" include "flib.h" xdef _library xdef chiplib@0000 T_OFFSET EQU 966 ; 6 + 32 * 30 ; This function executes a chip8 program of length d0.l stored at a0.l chiplib@0000: movem d0-d7/a0-a6,-(a7) ; Back up registers move.l d0,d6 ; Put length of chip8 program in d6 move.l a0,a5 ; Put address of chip8 program in a5 move.l #$1000,-(a7) ; Push size of chip8 mem ($1000 bytes) onto stack jsr tios::HeapAlloc ; Allocate memory lea 4(a7),a7 move.w d0,handle ; If there is not enough free mem, then beq exit ; Exit the program tios::DEREF d0,a6 ; Get address of handle move.l d6,-(a7) ; Push size of chip8 program pea 0(a5) ; Push address of chip8 program pea $200(a6) ; Push address where chip8 code will start jsr tios::memcpy ; Copy chip8 program to where chiplib will use it lea 12(a7),a7 move.l #80,-(a7) ; Push 5 bytes * 16 sprites = 80 bytes pea numbers(PC) ; Push address where number sprites start pea 0(a6) ; Push address where chip8 mem starts jsr tios::memcpy ; Copy sprites into chip8 memory lea 12(a7),a7 lea 80(a6),a5 ; Put address where jump stack will start in a5 move.l #0,regs move.l #0,regs+4 move.l #0,regs+8 move.l #0,regs+12 pea credits(PC) jsr tios::ST_showHelp lea 4(a7),a7 move.w #96,-(a7) move.w #176,-(a7) move.w #31,-(a7) move.w #47,-(a7) jsr flib::frame_rect lea 8(a7),a7 move.w #95,-(a7) move.w #175,-(a7) move.w #32,-(a7) move.w #48,-(a7) jsr flib::erase_rect lea 8(a7),a7 move.l #$700,d0 trap #1 bclr.b #2,$600001 move.l $64,oldint1 move.l #DelayRegTimer,$64 bset.b #2,$600001 trap #1 move.b #$00,mode move.l #$200,d7 bra run_opcode after_run: addi.w #2,d7 after_run_and_next_op: move.l #1500,d0 delay_loop: dbra d0,delay_loop move.w #$FEFF,$600018 nop nop nop nop nop nop nop nop move.b $60001B,d0 btst.b #6,d0 beq exit ;bra run_opcode run_opcode: clr.l d0 clr.l d1 clr.l d2 clr.l d3 clr.l d4 lea 1(a6,d7.l),a0 move.b (a0),d4 lsl.w #8,d4 move.b -(a0),d4 move.b d4,d0 lsr.b #4,d0 move.b d4,d1 andi.b #$0F,d1 rol.w #8,d4 move.b d4,d2 lsr.b #4,d2 move.b d4,d3 andi.b #$0F,d3 cmp.b #$0,d0 beq sys_func cmp.b #$1,d0 beq jump cmp.b #$2,d0 beq jump_to_sub cmp.b #$3,d0 beq skip_x_eq_c cmp.b #$4,d0 beq skip_x_ne_c cmp.b #$5,d0 beq skip_x_eq_y cmp.b #$6,d0 beq move_c_to_x cmp.b #$7,d0 beq add_c_to_x cmp.b #$8,d0 beq math cmp.b #$9,d0 beq skip_x_ne_y cmp.b #$A,d0 beq move_c_to_i cmp.b #$B,d0 beq jump_to_c_plus_v0 cmp.b #$C,d0 beq random cmp.b #$D,d0 beq sprite cmp.b #$E,d0 beq skip_key cmp.b #$F,d0 beq other sys_func: cmp.b #$E0,d4 beq cls cmp.b #$EE,d4 beq return cmp.b #$FB,d4 beq scroll_right cmp.b #$FC,d4 beq scroll_left cmp.b #$FD,d4 beq exit cmp.b #$FE,d4 beq mode_chip8 cmp.b #$FF,d4 beq mode_schip cmp.b #$0C,d2 ; Instruction form is 00CN, this *should* be d2 beq scroll_down bra illegal_instr jump: andi.w #$0FFF,d4 move.w d4,d7 bra after_run_and_next_op jump_to_sub: andi.w #$0FFF,d4 move.w d7,(a5)+ move.w d4,d7 bra after_run_and_next_op skip_x_eq_c: lea regs(PC),a0 adda.l d1,a0 cmp.b (a0),d4 bne after_run addi.w #4,d7 bra after_run_and_next_op skip_x_ne_c: lea regs(PC),a0 adda.l d1,a0 cmp.b (a0),d4 beq after_run addi.w #4,d7 bra after_run_and_next_op skip_x_eq_y: tst.b d3 bne illegal_instr lea regs(PC),a0 move.l a0,a1 adda.l d1,a0 adda.l d2,a1 cmpm.b (a0)+,(a1)+ ; +'s are to make it a valid instruction bne after_run addi.w #4,d7 bra after_run_and_next_op move_c_to_x: lea regs(PC),a0 adda.l d1,a0 move.b d4,(a0) bra after_run add_c_to_x: lea regs(PC),a0 adda.l d1,a0 add.b d4,(a0) bra after_run math: lea regs(PC),a0 move.l a0,a1 adda.l d1,a0 adda.l d2,a1 move.b (a1),d0 cmp.b #$0,d3 beq move_y_to_x cmp.b #$1,d3 beq or_y_into_x cmp.b #$2,d3 beq and_y_into_x cmp.b #$3,d3 beq xor_y_into_x cmp.b #$4,d3 beq add_y_to_x cmp.b #$5,d3 beq sub_y_from_x cmp.b #$6,d3 beq shift_right_x cmp.b #$7,d3 beq sub_x_from_y cmp.b #$E,d3 beq shift_left_x bra illegal_instr skip_x_ne_y: tst.b d3 bne illegal_instr lea regs(PC),a0 move.l a0,a1 adda.l d1,a0 adda.l d2,a1 move.b (a0),d0 move.b (a1),d1 cmp.b d0,d1 beq after_run addi.w #4,d7 bra after_run_and_next_op move_c_to_i: andi.w #$0FFF,d4 move.w d4,regi bra after_run jump_to_c_plus_v0: andi.w #$0FFF,d4 move.b regs(PC),d0 add.w d0,d4 move.w d4,d7 bra after_run_and_next_op random: move.w #$100,d0 jsr flib::random lea regs(PC),a0 adda.l d1,a0 and.b d4,d0 move.b d0,(a0) bra after_run sprite: cmp.b #$FF,mode beq sprite_high move.l a6,a0 ; Move the chip8 base mem address to a0 move.w regi(PC),d0 adda.l d0,a0 ; The address of the sprite is now in a0 lea regs(PC),a1 adda.l d1,a1 move.b (a1),d1 lea regs(PC),a1 adda.l d2,a1 move.b (a1),d2 andi.b #%00011111,d2 ; Calculate d2 % 32 ... the y coord is now in d2 move.l #LCD_MEM+T_OFFSET,a1 mulu #60,d2 ; 1 line = 30 bytes, each pixel is two pixels high adda.l d2,a1 ; The address of the screen line where the lea 14(a1),a4 move.b d1,d4 ; sprite starts is now in a1 andi.b #%00111000,d1 lsr.b #2,d1 adda.l d1,a1 ; The address of the video mem where the cmpa.l a1,a4 sne d2 ext.w d2 andi.b #%00000111,d4 ; sprite will start is now in a1 lsl.b #1,d4 sub.b #1,d3 clr.l d5 draw_loop: move.b (a0)+,d0 ror.l #1,d0 rol.b #1,d0 asr.b #1,d0 rol.l #2,d0 asr.b #1,d0 rol.l #2,d0 asr.b #1,d0 rol.l #2,d0 asr.b #1,d0 rol.l #2,d0 asr.b #1,d0 rol.l #2,d0 asr.b #1,d0 rol.l #2,d0 asr.b #1,d0 rol.l #2,d0 asr.b #1,d0 rol.l #2,d0 rol.l #8,d0 clr.w d0 ror.l d4,d0 and.w d2,d0 move.l d0,d1 and.l (a1),d1 cmp.l #0,d1 sne d1 or.b d1,d5 eor.l d0,(a1) eor.l d0,30(a1) lea 60(a1),a1 cmp.l #LCD_MEM+2880,a1 dbgt d3,draw_loop andi.b #1,d5 move.b d5,regs+15 bra after_run sprite_high: lea regs(PC),a0 move.l a0,a1 adda.l d1,a0 adda.l d2,a1 move.b (a0),d0 move.b (a1),d1 andi.b #$3F,d1 mulu #30,d1 move.l #LCD_MEM+T_OFFSET,a0 adda.l d1,a0 move.b d0,d2 andi.b #%01110000,d0 lsr.b #3,d0 clr.l d1 cmp.b #14,d0 sne d1 ext.w d1 andi.b #%00001111,d2 adda.l d0,a0 move.l a6,a1 move.w regi(PC),d5 adda.l d5,a1 move.b #0,d5 cmp.b #0,d3 beq draw_big_sprite sub.w #1,d3 draw_small_sprite: move.b (a1)+,d0 ror.l #8,d0 andi.l #$FF000000,d0 lsr.l d2,d0 and.w d1,d0 move.l d0,d4 and.l (a0),d4 cmp.l #0,d4 sne d4 or.b d4,d5 eor.l d0,(a0) adda.l #30,a0 cmp.l #LCD_MEM+2880,a0 dbgt d3,draw_small_sprite andi.b #$01,d5 move.b d5,regs+15 bra after_run draw_big_sprite: move.w #15,d3 draw_big_loop: move.b (a1)+,d0 lsl.w #8,d0 move.b (a1)+,d0 swap d0 clr.w d0 lsr.l d2,d0 and.w d1,d0 move.l d0,d4 and.l (a0),d4 cmp.l #0,d4 sne d4 or.b d4,d5 eor.l d0,(a0) adda.l #30,a0 cmp.l #LCD_MEM+2880,a0 dbgt d3,draw_big_loop andi.b #$01,d5 move.b d5,regs+15 bra after_run skip_key: cmp.b #$9E,d4 beq skip_pressed cmp.b #$A1,d4 beq skip_not_pressed bra illegal_instr other: cmp.b #$07,d4 beq x_eq_delay cmp.b #$0A,d4 beq wait_for_key cmp.b #$15,d4 beq delay_eq_x cmp.b #$18,d4 beq sound_eq_x cmp.b #$1E,d4 beq I_eq_I_plus_x cmp.b #$29,d4 beq point_I_to_char cmp.b #$33,d4 beq dobcd cmp.b #$55,d4 beq save_v0_to_vx cmp.b #$65,d4 beq load_v0_to_vx bra illegal_instr cls: move.w #95,-(a7) move.w #175,-(a7) move.w #32,-(a7) move.w #48,-(a7) jsr flib::erase_rect lea 8(a7),a7 bra after_run return: move.w -(a5),d7 bra after_run scroll_right: move.l #LCD_MEM+T_OFFSET,a0 move.w #63,d3 scroll_right_loop: move.l 12(a0),d0 lsr.l #4,d0 move.l 8(a0),d1 move.l d1,d2 lsr.l #4,d1 ror.l #4,d2 andi.l #$F0000000,d2 or.l d2,d0 move.l d0,12(a0) move.l 4(a0),d0 move.l d0,d2 lsr.l #4,d0 ror.l #4,d2 andi.l #$F0000000,d2 or.l d2,d1 move.l d1,8(a0) move.l (a0),d1 move.l d1,d2 lsr.l #4,d1 ror.l #4,d2 andi.l #$F0000000,d2 or.l d2,d0 move.l d0,4(a0) move.l d1,(a0) dbra d3,scroll_right_loop bra after_run scroll_left: move.l #LCD_MEM+T_OFFSET,a0 move.w #63,d3 scroll_left_loop: move.l (a0),d0 lsl.l #4,d0 move.l 4(a0),d1 move.l d1,d2 lsl.l #4,d1 rol.l #4,d2 andi.b #$0F,d2 or.b d2,d0 move.l d0,(a0) move.l 8(a0),d0 move.l d0,d2 lsl.l #4,d0 rol.l #4,d2 andi.b #$0F,d2 or.b d2,d1 move.l d1,4(a0) move.l 12(a0),d1 move.l d1,d2 lsl.l #4,d1 rol.l #4,d2 andi.b #$0F,d2 or.b d2,d0 move.l d0,8(a0) move.l d1,12(a0) add.l #30,a0 dbra d3,scroll_left_loop bra after_run mode_chip8: move.b #$00,mode bra after_run mode_schip: move.b #$FF,mode bra after_run scroll_down: move.w d3,d2 move.w #63,d1 sub.w d3,d1 move.l #LCD_MEM+T_OFFSET+1890,a0 move.l a0,a1 mulu #30,d2 suba.l d2,a1 scroll_loop: move.l (a1),(a0) move.l 4(a1),4(a0) move.l 8(a1),8(a0) move.l 12(a1),12(a0) sub.l #30,a0 sub.l #30,a1 dbra d1,scroll_loop move.l #LCD_MEM+T_OFFSET,a0 sub.w #1,d3 scroll_loop2: move.l #0,(a0) move.l #0,4(a0) move.l #0,8(a0) move.l #0,12(a0) add.l #30,a0 dbra d3,scroll_loop2 bra after_run move_y_to_x: move.b d0,(a0) bra after_run or_y_into_x: or.b d0,(a0) bra after_run and_y_into_x: and.b d0,(a0) bra after_run xor_y_into_x: eor.b d0,(a0) bra after_run add_y_to_x: add.b d0,(a0) scs regs+15 andi.b #1,regs+15 bra after_run sub_y_from_x: sub.b d0,(a0) scc regs+15 andi.b #1,regs+15 bra after_run shift_right_x: move.b (a0),d2 lsr.b #1,d2 scs regs+15 andi.b #1,regs+15 move.b d2,(a0) bra after_run sub_x_from_y: neg.b (a0) add.b d0,(a0) scs regs+15 andi.b #1,regs+15 bra after_run shift_left_x: move.b (a0),d2 lsl.b #1,d2 scs regs+15 andi.b #1,regs+15 move.b d2,(a0) bra after_run skip_pressed: lea regs(PC),a0 adda.l d1,a0 move.b (a0),d1 move.w #$FFFE,d0 lea keyrow(PC),a0 adda.l d1,a0 move.b (a0),d2 lea keycol(PC),a0 adda.l d1,a0 move.b (a0),d3 rol.w d2,d0 move.w d0,$600018 nop nop nop nop nop nop nop nop move.b $60001B,d1 btst.b d3,d1 bne after_run ; Branch if key was *not* pressed addi.w #4,d7 bra after_run_and_next_op skip_not_pressed: lea regs(PC),a0 adda.l d1,a0 move.b (a0),d1 move.w #$FFFE,d0 lea keyrow(PC),a0 adda.l d1,a0 move.b (a0),d2 lea keycol(PC),a0 adda.l d1,a0 move.b (a0),d3 rol.w d2,d0 move.w d0,$600018 nop nop nop nop nop nop nop nop move.b $60001B,d1 btst.b d3,d1 beq after_run ; Branch if key *was* pressed addi.w #4,d7 bra after_run_and_next_op x_eq_delay: lea regs(PC),a0 adda.l d1,a0 move.w dtimer(PC),d0 divu #6,d0 move.b d0,(a0) bra after_run wait_for_key: lea keytable(PC),a0 move.w #9,d0 move.w #$FFFE,d3 read_key_loop_start: move.w d3,$600018 nop nop nop nop nop nop nop nop move.b $60001B,(a0)+ rol.w #1,d3 dbra d0,read_key_loop_start lea keytable(PC),a0 lea keyrow+17(PC),a1 lea keycol+17(PC),a2 clr.l d3 move.w #16,d0 check_key_loop_start: move.l a0,a3 move.b -(a1),d3 move.b -(a2),d2 adda.l d3,a3 btst.b d2,(a3) dbeq d0,check_key_loop_start cmp.w #-1,d0 beq wait_for_key cmp.w #16,d0 beq exit lea regs(PC),a0 adda.l d1,a0 move.b d0,(a0) bra after_run delay_eq_x: lea regs(PC),a1 adda.l d1,a1 move.b (a1),d1 mulu #6,d1 move.w d1,dtimer bra after_run sound_eq_x: bra after_run I_eq_I_plus_x: lea regs(PC),a0 adda.l d1,a0 move.b (a0),d0 add.w d0,regi bra after_run point_I_to_char: lea regs(PC),a0 adda.l d1,a0 move.b (a0),d1 mulu #5,d1 move.w d1,regi bra after_run dobcd: move.w regi(PC),d0 move.l a6,a0 adda.l d0,a0 lea regs(PC),a1 adda.l d1,a1 move.b (a1),d1 divu #10,d1 swap d1 move.b d1,2(a0) clr.w d1 swap d1 divu #10,d1 swap d1 move.b d1,1(a0) clr.w d1 swap d1 move.b d1,(a0) bra after_run save_v0_to_vx: lea regs(PC),a0 move.w regi(PC),d0 move.l a6,a1 adda.l d0,a1 save_loop: move.b (a0)+,(a1)+ dbra d1,save_loop ;suba.l a6,a1 ;move.l a1,d0 ;move.w d0,regi bra after_run load_v0_to_vx: lea regs(PC),a0 move.w regi(PC),d0 move.l a6,a1 adda.l d0,a1 load_loop: move.b (a1)+,(a0)+ dbra d1,load_loop ;suba.l a6,a1 ;move.l a1,d0 ;move.w d0,regi bra after_run illegal_instr: bra after_run exit: move.l #$700,d0 trap #1 bclr.b #2,$600001 move.l oldint1(PC),$64 bset.b #2,$600001 trap #1 move.w handle(PC),-(a7) ; Push handle number jsr tios::HeapFree ; Free allocated memory lea 2(a7),a7 movem (a7)+,d0-d7/a0-a6 rts DelayRegTimer: movem d0,-(a7) move.w dtimer(PC),d0 beq AfterDec sub.w #1,d0 move.w d0,dtimer AfterDec: movem (a7)+,d0 rte keycol dc.b 6,5,6,7,5,6,7,5,6,7,5,7,7,0,4,1,6 keyrow dc.b 9,3,3,3,2,2,2,1,1,1,9,9,7,9,8,9,8 numbers: dc.b %11110000 ;0 dc.b %10010000 dc.b %10010000 dc.b %10010000 dc.b %11110000 dc.b %00100000 ;1 dc.b %01100000 dc.b %00100000 dc.b %00100000 dc.b %01110000 dc.b %11110000 ;2 dc.b %00010000 dc.b %11110000 dc.b %10000000 dc.b %11110000 dc.b %11110000 ;3 dc.b %00010000 dc.b %01110000 dc.b %00010000 dc.b %11110000 dc.b %10010000 ;4 dc.b %10010000 dc.b %11110000 dc.b %00010000 dc.b %00010000 dc.b %11110000 ;5 dc.b %10000000 dc.b %11110000 dc.b %00010000 dc.b %11110000 dc.b %11110000 ;6 dc.b %10000000 dc.b %11110000 dc.b %10010000 dc.b %11110000 dc.b %11110000 ;7 dc.b %00010000 dc.b %00010000 dc.b %00100000 dc.b %00100000 dc.b %11110000 ;8 dc.b %10010000 dc.b %11110000 dc.b %10010000 dc.b %11110000 dc.b %11110000 ;9 dc.b %10010000 dc.b %11110000 dc.b %00010000 dc.b %11110000 dc.b %01100000 ;A dc.b %10010000 dc.b %11110000 dc.b %10010000 dc.b %10010000 dc.b %11100000 ;B dc.b %10010000 dc.b %11100000 dc.b %10010000 dc.b %11100000 dc.b %11110000 ;C dc.b %10000000 dc.b %10000000 dc.b %10000000 dc.b %11110000 dc.b %11100000;D dc.b %10010000 dc.b %10010000 dc.b %10010000 dc.b %11100000 dc.b %11110000 ;E dc.b %10000000 dc.b %11100000 dc.b %10000000 dc.b %11110000 dc.b %11110000 ;F dc.b %10000000 dc.b %11100000 dc.b %10000000 dc.b %10000000 dc.b %01111110 ;0 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %01111110 dc.b %00001000 ;1 dc.b %00011000 dc.b %00101000 dc.b %00001000 dc.b %00001000 dc.b %00001000 dc.b %00001000 dc.b %00001000 dc.b %00001000 dc.b %11111111 dc.b %00111100 ;2 dc.b %01000010 dc.b %10000001 dc.b %00000001 dc.b %00000010 dc.b %00000100 dc.b %00001000 dc.b %00010000 dc.b %00100000 dc.b %11111111 dc.b %00111100 ;3 dc.b %01000010 dc.b %10000001 dc.b %00000001 dc.b %00001110 dc.b %00001110 dc.b %00000001 dc.b %10000001 dc.b %01000010 dc.b %00111100 dc.b %10000100 ;4 dc.b %10000100 dc.b %10000100 dc.b %10000100 dc.b %11111111 dc.b %00000100 dc.b %00000100 dc.b %00000100 dc.b %00000100 dc.b %00000100 dc.b %11111111 ;5 dc.b %10000000 dc.b %10000000 dc.b %10111100 dc.b %11000010 dc.b %10000001 dc.b %00000001 dc.b %10000001 dc.b %01000010 dc.b %00111100 dc.b %00111100 ;6 dc.b %01000000 dc.b %10000000 dc.b %10000000 dc.b %10111100 dc.b %11000010 dc.b %10000001 dc.b %10000001 dc.b %01000010 dc.b %00111100 dc.b %11111111 ;7 dc.b %00000001 dc.b %00000001 dc.b %00000010 dc.b %00000010 dc.b %00000100 dc.b %00000100 dc.b %00001000 dc.b %00001000 dc.b %00001000 dc.b %01111110 ;8 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %01111110 dc.b %01111110 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %01111110 dc.b %01111110 ;9 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %01111111 dc.b %00000001 dc.b %00000001 dc.b %00000001 dc.b %00000010 dc.b %01111100 dc.b %00011000 ;A dc.b %00100100 dc.b %01000010 dc.b %10000001 dc.b %11111111 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %11111110 ;B dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %11111110 dc.b %11111110 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %11111110 dc.b %01111110 ;C dc.b %10000001 dc.b %10000001 dc.b %10000000 dc.b %10000000 dc.b %10000000 dc.b %10000000 dc.b %10000001 dc.b %10000001 dc.b %01111110 dc.b %11111100 ;D dc.b %10000010 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %10000001 dc.b %10000010 dc.b %11111100 dc.b %11111111 ;E dc.b %10000000 dc.b %10000000 dc.b %10000000 dc.b %11111000 dc.b %11111000 dc.b %10000000 dc.b %10000000 dc.b %10000000 dc.b %11111111 dc.b %11111111 ;F dc.b %10000000 dc.b %10000000 dc.b %10000000 dc.b %11111000 dc.b %10000000 dc.b %10000000 dc.b %10000000 dc.b %10000000 dc.b %10000000 credits dc.b "schip emulator v0.9B by Noah Stein",0 _library dc.b "chiplib",0 ; Comment out for PlusShell compiling bss oldint1 dc.l 0 handle dc.w 0 dtimer dc.w 0 regi dc.w 0 regs ds.b 16 keytable ds.b 10 mode dc.b 0 end