Bugs disclosure

A project log for TMS0800 FPGA implementation in VHDL

Inspired by and I set out to do similar in VHDL, for MicroNova Mercury board.

zpekiczpekic 09/22/2019 at 18:020 Comments

Few annoying bugs I was unable to track down and fix so far. Note that none of them affect the calculations due to workarounds or fixes applies.

1. Ghost digits on 7-seg leds

These appear on any CPU frequency higher that 50kHz or so, but only in the calculator display path, not when displaying the internal debug content of macro and microprogram counters. The display strobing goes from higher digits to lower (this is necessary because only this way the leading zero surpression can work) and it appears the segments "race ahead" of the digit strobes. 

This bug renders 7 seg display unusable for normal calculator operations. The workaround is to use VGA only to display entry and results, which is no great loss as 4 digit 7-seg can only display half of the digits at a time, one has to press BTN0 to switch to upper half, making it very impractical to begin with.

2. Not all digits of internal registers displayed on VGA debug screen

TMS0800 registers are either 11 BCD digits long (A, B, C), or 11 bits long (AF, BF). There is a timing problem in VGA debug component or its driver which "prints" only:

9 last digits at 12.5MHz

10 last digits at 6.25MHz

At lower frequencies (57.6kHz and 1kHz) all digits are printed on the screen. None of this impacts operation of the calculator core, except makes first 1-2 digits (containing sign etc.) invisible in debug mode.

3. Stray code execution

There is no unconditional jump in TMS0800 - those are modeled by "knowing" the state of CF flag and executing "jump on condition set/reset" instructions accordingly. However due to probably another unknown bug, in Sinclair mode at the end of ROM (part of AntiLog evaluation routine) CF is sometimes not the expected "1". This has been "fixed" with a hack explained in the code, but the real fix should be in some underlying issue (perhaps microcode implementation):

prog_sinclair: rom512x12

generic map
-- HACKHACK: Why is this "random instruction" being passed in to intitialize the ROM???
-- Last instruction in Sinclair ROM as 0x13F is "BINE ALOGDIV" - however in some cases
-- CF is 0 which means it will not be executed and execution will continue at 0x140, which 
-- will bring it back to the right place, instead of executing bad opcodes, or NOPs which 
-- would wrap up to reset location 0. This is indication of another bug but for now this
-- "works". 
fill_value => BIE_ALOGDIV,
sinclair_mode => true, -- hint to show correct disassembled listing (Sinclair mode)
asm_filename => "./sourceCode_sinclair.asm",
lst_filename => "./tms0800/output/sourceCode_sinclair.lst"
port map
address => pc,
data => instruction_sinclair

4. Numeric digit keyboard off by one

Each TMS0800 instruction "pulls" the next right keyboard scan line down. That is the reason why the sequence of instructions is arranged exactly like the keys on the keyboard:

0 0111 0001 0071 10 00000 0000          BKO    CLEAR  ; Clear key pressed?
0 0111 0010 0072 10 00101 0101          BKO    EQLKEY ; Equal key pressed?
0 0111 0011 0073 10 00100 1101          BKO    PLSKEY ; Plus key pressed?
0 0111 0100 0074 10 00011 1101          BKO    MINKEY ; Minus key pressed?
0 0111 0101 0075 10 00100 1100          BKO    MLTKEY ; Mult key pressed?
0 0111 0110 0076 10 00100 1011          BKO    DIVKEY ; Divide key pressed?
0 0111 0111 0077 10 00110 0011          BKO    CEKEY  ; CE key pressed?
0 0111 1000 0078 10 00101 1101          BKO    DPTKEY ; Decimal point key pressed?
0 0111 1001 0079 10 00111 1110          BKO    ZERKEY ; Zero key pressed?
0 0111 1010 007A 11 10011 1111          EXAB   ALL    ; Process digit key...
0 0111 1011 007B 11 11010 0010          AKCN   LSD1   ; Count key position into A

After the EXAB, the scan should wrap around and start with key "1", which if detected would increment register A in the end accumulating the count until a key was found (so it would return with 8 if key down contact was sensed when 8th line from left was pulled down etc.). However, due to some timing bug sometimes the count starts off by one, so pressing "3" registers as "2" etc. The "fix" is a hack in microcode to double check in AKCN that right scan line is down before proceeding:

118 => -- AKCN 
--uc_ss(ss_off) or
uc_reg(bcd_fromalu) or
uc_if(cond_e11, upc_next, uc_label(121)),
119 =>
--uc_ss(ss_off) or
uc_sam(sam_update) or
uc_alu(fun_adcbcd) or
uc_reg(bcd_fromalu) or
120 =>
--uc_ss(ss_off) or
uc_e(e_rol) or
121 => -- if kn was down, means we have a correct count in last mantissa, so bail, otherwise continue
--uc_ss(ss_off) or
uc_if(cond_kn, upc_next, uc_label(CONTINUE)),
122 => -- if scanned all, bail with CF = 1 to indicate no key
--uc_ss(ss_off) or
uc_if(cond_digit10, uc_label(CONTINUECS), uc_label(FORK)), 

-- HACKHACK: make sure we are at last digit before continuing!
123 =>
--uc_ss(ss_off) or
uc_if(cond_digit10, uc_label(CONTINUE), upc_next),

124 => 
--uc_ss(ss_off) or
uc_sync(pulse) or