1
0
Fork 0
2021-8bit-cpu-gameboy/code/tick.asm

295 lines
4.1 KiB
NASM

; [in] a: value to calculate
; [out] e: 100s place
; [out] c: 10s place
; [out] d: 1s place
CalculateBCD:
ld e, 0 ; count (100s place)
ld b, 100
.sub_led_100:
ld h, a ; save for 10s calc
sub b
jr c, .reset
; count++
ld c, a
ld a, e
inc a
ld e, a
ld a, c
jr .sub_led_100
.reset:
ld b, 10
ld c, 0 ; count (10s place)
ld a, h
.sub_led_10:
sub b
jr c, .mod
; count++
ld d, a
ld a, c
inc a
ld c, a
ld a, d
jr .sub_led_10
.mod:
; a contains overflow; difference is remainder ($ff-remainder-9)
ld b, a
ld a, $ff
sub b
ld b, a
ld a, 9
sub b
; 1s place
ld d, a
ret
; [in] a: Value to LED-ify
; [in] hl: Start address of LED tile
; [in] c: LED count
UpdateLeds:
ld b, a ; cached A
dec hl ; pre-decrement, will be added back first way around the loop
ld de, $00FF ; offset from HL (inc e will overflow to 0 for first offset)
; shift left for anything less than a count of 8 so we have MSB in the right spot for left shift
ld a, 8
sub c
jr z, .check
.shift:
sla b
dec a
jr nz, .shift
.check:
inc hl
; determine if we've checked all bits
inc e
ld a, e
cp c
jr z, .exit
; shift left, carry contains whether the bit was set
sla b
jr c, .set
.clear:
ld [hl], LED_OFF
jr .check
.set:
ld [hl], LED_ON
jr .check
.exit:
ret
Tick:
; We don't want any interrupts mid-tick
di
; Reset the control signals
xor a
ld hl, ctrl
ld [hl+], a
ld [hl], a
; Reset bus; in case we have no control signals
ld [bus], a
; Operate based on current op stage
ld a, [opc]
ld c, a
; Stage 0 and 1 are the same for all operations
cp $0
jr z, .stage_0
cp $1
jr z, .stage_1
; Stage 2, 3, and 4 differ
jr .stage_2_3_4
; Stage 0 same for all instructions: CO | MI
.stage_0:
ld hl, ctrl
set CTRL_MI, [hl]
inc hl
set CTRL_CO, [hl]
jr .update_leds
; Stage 1 same for all instructions: RO | II | CE
.stage_1:
ld hl, ctrl
set CTRL_RO, [hl]
set CTRL_II, [hl]
inc hl
set CTRL_CE, [hl]
jr .update_leds
.stage_2_3_4:
ld a, [ir]
and $f0
ld b, c
; NOP
cp $00
jr z, .update_leds
; LDA
cp $10
call z, op_lda
; ADD
cp $20
call z, op_add
; SUB
cp $30
call z, op_sub
; STA
cp $40
call z, op_sta
; LDI
cp $50
call z, op_ldi
; JP
cp $60
call z, op_jmp
; JC
cp $70
call z, op_jc
; JZ
cp $80
call z, op_jz
; OUT
cp $e0
call z, op_out
; HLT
cp $f0
call z, op_hlt
.update_leds:
; These LEDs are latched by the clock so we want to display only pre-control signal evaluation
ld a, [opc]
ld c, 3
ld hl, opc_led_2
call UpdateLeds
ld a, [pc]
ld c, 4
ld hl, pc_led_3
call UpdateLeds
ld a, [mar]
ld c, 4
ld hl, mar_led_3
call UpdateLeds
ld a, [ir]
ld c, 8
ld hl, ir_led_7
call UpdateLeds
ld a, [a_reg]
ld c, 8
ld hl, a_led_7
call UpdateLeds
ld a, [b_reg]
ld c, 8
ld hl, b_led_7
call UpdateLeds
ld a, [alu]
ld c, 8
ld hl, alu_led_7
call UpdateLeds
ld a, [ctrl]
ld c, 8
ld hl, ctrl_led_ht
call UpdateLeds
ld a, [ctrl+1]
ld c, 8
ld hl, ctrl_led_eo
call UpdateLeds
ld a, [flags]
ld c, 2
ld hl, flag_led_c
call UpdateLeds
; Update output display
ld a, [out_reg]
call CalculateBCD
; 100s place
ld hl, display
ld [hl], e
; 10s place
inc hl
ld [hl], c
; 1s place
inc hl
ld [hl], d
; Update mem display
ld a, [mar]
ld hl, ram
add l
ld l, a
ld a, [hl]
ld [mem], a
; Update the control signals for next time. Order matters.
call ctrl_ht
call ctrl_ro
call ctrl_io
call ctrl_ao
call ctrl_su
call ctrl_fi
call ctrl_eo
call ctrl_co
call ctrl_mi
call ctrl_ri
call ctrl_ii
call ctrl_ai
call ctrl_bi
call ctrl_oi
call ctrl_ce
call ctrl_jp
; The memory LEDs are also instantaneous (they display whatever is in memory at the address in the MAR)
ld a, [mem]
ld c, 8
ld hl, mem_led_7
call UpdateLeds
; The bus LEDs are instantaneous (no latching), so we should always display latest
ld a, [bus]
ld c, 8
ld hl, bus_led_7
call UpdateLeds
; Go to next op stage
ld a, [opc]
inc a
ld [opc], a
cp 5
jr nz, .exit
xor a
ld [opc], a
.exit:
ei
ret