package android.emu6502 import android.emu6502.instructions.Symbols import com.google.common.collect.ImmutableList import org.hamcrest.CoreMatchers.equalTo import org.junit.Assert.assertThat import org.junit.Test import org.mockito.Mockito.mock class CPUTest { private val memory = Memory(mock(Display::class.java)) private val assembler = Assembler(memory, Symbols()) private val cpu = CPU(memory) @Test fun testSimple() { val lines = ImmutableList.of( "LDA #$01", "STA $0200", "LDA #$05", "STA $0201", "LDA #$08", "STA $0202") assembler.assembleCode(lines) cpu.testRun() assertThat(cpu.A, equalTo(0x08)) assertThat(cpu.X, equalTo(0x00)) assertThat(cpu.Y, equalTo(0x00)) assertThat(cpu.SP, equalTo(0xFF)) assertThat(cpu.PC, equalTo(0x0610)) assertThat(cpu.flags(), equalTo("00110000")) } @Test fun testWithComments() { val lines = ImmutableList.of( "LDA #\$c0 ;Load the hex value \$c0 into the A register", "TAX ;Transfer the value in the A register to X", "INX ;Increment the value in the X register", "ADC #\$c4 ;Add the hex value \$c4 to the A register", "BRK ;Break - we're done") assembler.assembleCode(lines) cpu.testRun() assertThat(cpu.A, equalTo(0x84)) assertThat(cpu.X, equalTo(0xC1)) assertThat(cpu.Y, equalTo(0x00)) assertThat(cpu.SP, equalTo(0xFF)) assertThat(cpu.PC, equalTo(0x0607)) assertThat(cpu.flags(), equalTo("10110001")) } @Test fun testBranchAndLabel() { val lines = ImmutableList.of( "LDX #$08", "decrement:", "DEX", "STX $0200", "CPX #$03", "BNE decrement", "STX $0201", "BRK") assembler.assembleCode(lines) cpu.testRun() assertThat(cpu.A, equalTo(0x00)) assertThat(cpu.X, equalTo(0x03)) assertThat(cpu.Y, equalTo(0x00)) assertThat(cpu.SP, equalTo(0xFF)) assertThat(cpu.PC, equalTo(0x060e)) assertThat(cpu.flags(), equalTo("00110011")) } @Test fun testJump() { val lines = ImmutableList.of( "LDA #$03", "JMP there", "BRK", "BRK", "BRK", "there:", "STA $0200") assembler.assembleCode(lines) cpu.testRun() assertThat(cpu.A, equalTo(0x03)) assertThat(cpu.X, equalTo(0x00)) assertThat(cpu.Y, equalTo(0x00)) assertThat(cpu.SP, equalTo(0xFF)) assertThat(cpu.PC, equalTo(0x060c)) assertThat(cpu.flags(), equalTo("00110000")) } @Test fun testJumpToSubroutines() { val lines = ImmutableList.of( "JSR init", "JSR loop", "JSR end", "init:", "LDX #$00", "RTS", "loop:", "INX", "CPX #$05", "BNE loop", "RTS", "end:", "BRK") assembler.assembleCode(lines) cpu.testRun() assertThat(cpu.A, equalTo(0x00)) assertThat(cpu.X, equalTo(0x05)) assertThat(cpu.Y, equalTo(0x00)) assertThat(cpu.SP, equalTo(0xFD)) assertThat(cpu.PC, equalTo(0x0613)) assertThat(cpu.flags(), equalTo("00110011")) } @Test fun testSymbols() { val lines = ImmutableList.of( "define a_dozen $0c ; a constant", "LDX #a_dozen ; equivalent to \"LDX #$0c\"") assembler.assembleCode(lines) cpu.testRun() assertThat(cpu.A, equalTo(0x00)) assertThat(cpu.X, equalTo(0x0C)) assertThat(cpu.Y, equalTo(0x00)) assertThat(cpu.SP, equalTo(0xFF)) assertThat(cpu.PC, equalTo(0x0603)) assertThat(cpu.flags(), equalTo("00110000")) } @Test fun testSnake() { val lines = ImmutableList.of( "define appleL $00 ; screen location of apple, low byte", "define appleH $01 ; screen location of apple, high byte", "define snakeHeadL $10 ; screen location of snake head, low byte", "define snakeHeadH $11 ; screen location of snake head, high byte", "define snakeBodyStart $12 ; start of snake body byte pairs", "define snakeDirection $02 ; direction (possible values are below)", "define snakeLength $03 ; snake length, in bytes", "; Directions (each using a separate bit)", "define movingUp 1", "define movingRight 2", "define movingDown 4", "define movingLeft 8", "; ASCII values of keys controlling the snake", "define ASCII_w $77", "define ASCII_a $61", "define ASCII_s $73", "define ASCII_d $64", "; System variables", "define sysRandom \$fe", "define sysLastKey \$ff", " jsr init", " jsr loop", "init:", " jsr initSnake", " jsr generateApplePosition", " rts", "initSnake:", " lda #movingRight ;start direction", " sta snakeDirection", " lda #4 ;start length (2 segments)", " sta snakeLength", " ", " lda #$11", " sta snakeHeadL", " ", " lda #$10", " sta snakeBodyStart", " ", " lda #$0f", " sta $14 ; body segment 1", " ", " lda #$04", " sta snakeHeadH", " sta $13 ; body segment 1", " sta $15 ; body segment 2", " rts", "generateApplePosition:", " ;load a new random byte into $00", " lda sysRandom", " sta appleL", " ;load a new random number from 2 to 5 into $01", " lda sysRandom", " and #$03 ;mask out lowest 2 bits", " clc", " adc #2", " sta appleH", " rts", "loop:", " jsr readKeys", " jsr checkCollision", " jsr updateSnake", " jsr drawApple", " jsr drawSnake", " jsr spinWheels", " jmp loop", "readKeys:", " lda sysLastKey", " cmp #ASCII_w", " beq upKey", " cmp #ASCII_d", " beq rightKey", " cmp #ASCII_s", " beq downKey", " cmp #ASCII_a", " beq leftKey", " rts", "upKey:", " lda #movingDown", " bit snakeDirection", " bne illegalMove", " lda #movingUp", " sta snakeDirection", " rts", "rightKey:", " lda #movingLeft", " bit snakeDirection", " bne illegalMove", " lda #movingRight", " sta snakeDirection", " rts", "downKey:", " lda #movingUp", " bit snakeDirection", " bne illegalMove", " lda #movingDown", " sta snakeDirection", " rts", "leftKey:", " lda #movingRight", " bit snakeDirection", " bne illegalMove", " lda #movingLeft", " sta snakeDirection", " rts", "illegalMove:", " rts", "checkCollision:", " jsr checkAppleCollision", " jsr checkSnakeCollision", " rts", "checkAppleCollision:", " lda appleL", " cmp snakeHeadL", " bne doneCheckingAppleCollision", " lda appleH", " cmp snakeHeadH", " bne doneCheckingAppleCollision", " ;eat apple", " inc snakeLength", " inc snakeLength ;increase length", " jsr generateApplePosition", "doneCheckingAppleCollision:", " rts", "checkSnakeCollision:", " ldx #2 ;start with second segment", "snakeCollisionLoop:", " lda snakeHeadL,x", " cmp snakeHeadL", " bne continueCollisionLoop", "maybeCollided:", " lda snakeHeadH,x", " cmp snakeHeadH", " beq didCollide", "continueCollisionLoop:", " inx", " inx", " cpx snakeLength ;got to last section with no collision", " beq didntCollide", " jmp snakeCollisionLoop", "didCollide:", " jmp gameOver", "didntCollide:", " rts", "updateSnake:", " ldx snakeLength", " dex", " txa", "updateloop:", " lda snakeHeadL,x", " sta snakeBodyStart,x", " dex", " bpl updateloop", " lda snakeDirection", " lsr", " bcs up", " lsr", " bcs right", " lsr", " bcs down", " lsr", " bcs left", "up:", " lda snakeHeadL", " sec", " sbc #$20", " sta snakeHeadL", " bcc upup", " rts", "upup:", " dec snakeHeadH", " lda #$1", " cmp snakeHeadH", " beq collision", " rts", "right:", " inc snakeHeadL", " lda #$1f", " bit snakeHeadL", " beq collision", " rts", "down:", " lda snakeHeadL", " clc", " adc #$20", " sta snakeHeadL", " bcs downdown", " rts", "downdown:", " inc snakeHeadH", " lda #$6", " cmp snakeHeadH", " beq collision", " rts", "left:", " dec snakeHeadL", " lda snakeHeadL", " and #$1f", " cmp #$1f", " beq collision", " rts", "collision:", " jmp gameOver", "drawApple:", " ldy #0", " lda sysRandom", " sta (appleL),y", " rts", "drawSnake:", " ldx #0", " lda #1", " sta (snakeHeadL,x) ; paint head", " ", " ldx snakeLength", " lda #0", " sta (snakeHeadL,x) ; erase end of tail", " rts", "spinWheels:", " ldx #0", "spinloop:", " nop", " nop", " dex", " bne spinloop", " rts", "gameOver:", "\n") assembler.assembleCode(lines) cpu.testRun() assertThat(cpu.A, equalTo(0x1f)) assertThat(cpu.X, equalTo(0xff)) assertThat(cpu.Y, equalTo(0x00)) assertThat(cpu.SP, equalTo(0xfb)) assertThat(cpu.PC, equalTo(0x0736)) assertThat(cpu.flags(), equalTo("00110011")) } }