Vintage PC pages
Home -> Vintage PCs -> Husky Hunter 2

The Husky Hunter 2

Note: This page is mainly based on dumping the ROMs of a Hunter 2 that no longer boots; so there's a fair bit of guesswork that I haven't been able to test on a working computer.


The Husky Hunter 2 is a small portable computer, designed to be rugged and reasonably waterproof. The case is sealed with a rubber gasket, and the interior contains a packet of silica gel to keep it dry.

Power is provided by four AA batteries, or an external power supply. An internal rechargeable battery maintains the memory while the main batteries are being changed; if that runs down, the computer won't boot. Letting it recharge from the main batteries overnight may help to bring the computer back to life.

The hardware includes:

Memory Map

The Z80's 64k of address space is divided into three windows:

C000Common RAM memory
8000Banked ROM or RAM
4000ROM 0 or RAM

Memory paging is accomplished by writing to port 0E0h.

Values written are:

ValueAt 0000At 4000At 8000At C000
00-09ROM 0Banked ROM. Low 4 bits of value give bank:
0 => ROM 1, first 16k
1 => ROM 1, second 16k
2 => ROM 2, first 16k
3 => ROM 2, second 16k
Common RAM
80-8FBanked RAM. Low 4 bits of value give the bank to use.


The keycaps on my Hunter don't really seem to match the ASCII that the system ROM generates. The Hunter pictured here seems a much closer match for the codes I'm getting.

The keyboard matrix is a 7×8 grid, laid out like this:

Column 0 1 2 3 4 5 6 7
2 09PO LKCommaSpace
3 Esc/BrkUpRightLbl/Ins EnterDownBackspaceTab
4 =-Left[ ';/.
5 21WQ ACtrlZShift

The ROM also contains a second, smaller, key table, for the SP and M-208. This has one fewer row and omits the cursor and symbol keys, and lower-case letters. Unshifted letter keys generate capital letters, and shift+letter generates a symbol. The matrix for this keyboard is:

Column 0 1 2 3 4 5 6 7
2 09PO LKSpace
3 Enter
5 21WQ ACtrlZShift

I/O Ports

The following I/O port ranges are used:

00-1F: NSC810 I/O and Timer

The NSC810 provides timing for the serial port, and access to the keyboard.

00Port A DataOn input: Reads the current keyboard matrix row(s) as selected by writing to port B, (see below). Results are active low, 0 if key pressed.
01Port B DataOn input: Bit 7 gives state of RS232 DSR line.

On output: Used to select keyboard matrix row. To read a single row, enable output on only one bit, and write 0 to that bit. For example, write 02h to port 5, then ~(02h) = 0FEh to port 1. At end of keyboard read, write 7Fh to port 5 and 80h to port 1; reads from port 0 then check all rows.
02Port C Data On input:
Bit 0 gives state of RS232 data received line?
Bit 1 gives state of RS232 DCD line.
Bit 2 set if battery low?
Bit 3: ?
Bit 5: Power button pressed?
04 Data Direction Register A Initialised to 0 (all lines input)
05 Data Direction Register B Initialised to 7Fh (bit 7 input, others output)
06 Data Direction Register C Initialised to 20h (bit 5 output, others input)
07 Mode Definition Register Initialised to 0 (normal mode)
08 Port A: Clear bit(s)Write the mask of bits to clear
09 Port B: Clear bit(s)Write the mask of bits to clear
0A Port C: Clear bit(s)Write the mask of bits to clear
0C Port A: Set bit(s)Write the mask of bits to set
0D Port B: Set bit(s)Write the mask of bits to set
0E Port C: Set bit(s)Write the mask of bits to set
10 Timer 0 low
11 Timer 0 high
12 Timer 1 low
13 Timer 1 high
14 Stop timer 0
15 Start timer 0
16 Stop timer 1
17 Start timer 1
18 Timer 0 mode
19 Timer 1 mode

The timer runs at the same speed as the CPU, 4MHz.

If the serial port is set to use IBM 2780 protocol, then timer 0 is used in one-shot mode with prescale = 2. Otherwise it's used in 'event counter' mode to provide the transmit baud timings.

Timer 1 provides the receive baud timings.

20-3F: HD61830 LCD Controller

The Hunter uses an HD61830 to drive its LCD screen. The processor can only update the screen by sending commands to the HD61830 through its I/O ports; the HD61830's video RAM is internal to it and does not appear in the Z80's memory space.

20On read: Busy if bit 0 is set. Before accessing any other port check that bit 0 of this register is 0.
On write: Parameter for current command.
21On write: Command. All commands bar 0Dh are followed by a write to port 20h with the parameter for the command.
3EByte read from screen by command 0Dh. The HD61830 datasheet cautions that a correct value is only returned on the second read.

Commands are:

00 Mode control. Bits in value include:
Bit 1: Graphics mode
Bit 5: Display enabled
01 Set character dimensions. Bits in value are:
Bits 2-0: Character width - 1
Bits 7-4: Character height - 1
02 Set width of screen in characters. value is width - 2.
03 Set time divisions.
04 Set cursor row. value is position of cursor in character cell.
08 Set address of screen start in framebuffer (low byte).
09 Set address of screen start in framebuffer (high byte).
0A Set address of cursor in framebuffer (low byte).
0B Set address of cursor in framebuffer (high byte).
0C Write value to video RAM at the cursor location, and increment the cursor location by 1.
0D Read video RAM at the cursor location. Value will be returned through port 3Eh.
0E Clear a bit in video RAM at the cursor location, and increment the cursor location by 1. value gives the number of the bit.
0F Set a bit in video RAM at the cursor location, and increment the cursor location by 1. value gives the number of the bit.

40-4F: MM58174A Real-Time Clock

The clock's 15 4-bit registers are visible to the Z80 as:

40: Test register
41: Tenths of a second
42: Seconds low  (BCD digit)
43: Seconds high (BCD digit)
44: Minutes low  (BCD digit)
45: Minutes high (BCD digit)
46: Hours low    (BCD digit)
47: Hours high   (BCD digit)
48: Days low     (BCD digit)
49: Days high    (BCD digit)
4A: Day of week  (BCD digit)
4B: Months low   (BCD digit)
4C: Months high  (BCD digit)
4D: Leap year shift register  
4E: Start/stop
4F: Interrupt

60: LCD backlight and contrast

I'm more or less guessing from the code, but it looks as if the bit mapping is:

Bit 2: Backlight toggle
Bits 1,0,7,6: Contrast level.

80-86: Serial port and system control

80: Written by NMI handler — presumably a handshake line of some kind.
81: Bit 0 = transmit data line
82: Bit 0 = CTS? 
83: First action of the boot ROM is to write 55h to this port. Later on
    in the boot process, 1 is written.
    Write 0 to power off.
84: Bit 0 = RTS
86: Bit 0 controls the beeper.

BB: NSC800 interrupt control register

Bit 0 = Enable normal interrupts
Bit 1 = Enable RSTC interrupts 
Bit 2 = Enable RSTB interrupts
Bit 3 = Enable RSTA interrupts 

The INT interrupt is used only in IBM 2780 communications.

As far as I can see, the RSTC interrupt is used when the serial port is using CTS handshaking. It fires when the CTS signal is received, and signals the NMI handler to start transmitting a character (if there is one).

The RSTB interrupt appears to fire when activity is detected on the serial port's receive line. The interrupt handler sets up the correct baud rate on timer channel 1; the RSTA handler then deals with decoding the bits as they come in.

The RSTA interrupt is connected to timer channel 1, and fires when the timer reaches 0. When receiving a byte from the serial port, it is used to generate bit timings. Otherwise it is runs at its minimum speed, approximately 61Hz.

The NMI is connected to timer channel 0, and fires when the timer reaches 0. This is used in serial output; the NMI timing matches the rate at which bits should be transmitted.


The operating system is DEMOS (in my case, DEMOS 2.21 build 9G08h V4), which uses an API moderately compatible with CP/M 2.2. Extra BDOS functions are used to access Hunter-specific facilities.

DEMOS supports CP/M BDOS calls, but there is no BIOS jumpblock or separate CCP.

For a program running under DEMOS, the memory map is as follows:

E000DEMOS resident portion (corresponding to CP/M BDOS)
DF80Operating system variables
D80080×24 character buffer — the LCD is used to display a 6×40 window into this area.
D700DEMOS jumpblock. First jump is to BDOS entry point.
0000Zero page

DEMOS Filesystem

Memory banks 1+ are used for the RAMdisc. Each memory bank is laid out as:

0800Data blocks
0040[in bank 1 only] Used space bitmap, 1 bit / block
0000Interrupt vectors

Unlike in a real CP/M filesystem, the directory is spread across however many RAM banks there are, with 56 entries in each bank.

The filesystem uses 2k blocks; block nn is at address ((nn * 0x800) & 0xFFFF) in RAM bank (nn / 0x20). This means block numbers are not contiguous across the filesystem; blocks 0x00-0x1F correspond to the TPA, 0x20 / 0x40 / 0x60 etc to the directory zones, 0x38-0x3F / 0x58-0x5F / 0x78-0x7F etc to common memory. In the used space bitmap, these blocks will be marked as used.

A DEMOS directory entry is formed:

UU F1 F2 F3 F4 F5 F6 F7 F8 T1 T2 T3 EX S1 S2 RC   .FILENAMETYP....
AL AL AL AL AL AL AL AL AL AL AL AL AL AL AL AL   ................

UU is 0 for a valid file, 0E5h for a deleted file. DEMOS does not support
CP/M user numbers.
Fn - filename
Tn - filetype. File attributes as for CP/M 2.2.
EX - Extent counter, for files more than 16k in size.
S1 } Extent checksum on last read.
S2 } Extent checksum on last write.
AL - Eight 16-bit words giving the numbers of 2k blocks containing the file 

The checksum is an 8-bit sum, calculated on the first 2047 bytes of each block covered by this extent. A file may only be 128 bytes long, but the checksum covers the entire 2k block containing it (omitting the last byte).

When an extent is opened its S1 checksum is calculated. If this does not match the S2 checksum recorded by the last save, the attempt to open the file will fail.

DEMOS jumpblock

The jumpblock at D700h is an approximation to the CP/M BIOS jumpblock:

	jp	0E000h	;DEMOS entry point
	jp	bdos0	;Wrapper for BDOS function 0
	jp	wboot	;Launch command processor
	jp	const	;Get console status
	jp	conin	;Read character with no echo
	jp	conout	;Console output, C = character.
	jp	list	;Output to serial port, C = character.
	jp	punch	;Output to serial port, C = character.
	jp	reader	;Input from serial port
	jp	home	;Returns A=0
	jp	seldsk	;Returns A=0
	jp	settrk	;Returns A=0
	jp	setsec	;Returns A=0
	jp	setdma	;Sets DMA address in BC
	jp	read	;Wrapper for BDOS function 14h, and not in any way
			;the low-level sector read that the CP/M BIOS provides.
	jp	write	;Wrapper for BDOS function 15h, and not in any way
			;the low-level sector write that the CP/M BIOS provides.

Z80 restarts and other addresses in low memory

Cold boot. This code occupies 5 bytes, so there is no I/O byte or current CCP drive. Since there is no jump at 0000h, programs which expect to find the address of the BIOS at 0001h will not work correctly.
BDOS entry
RST 38h followed by a jump to the BDOS entry point.
RST 10h
Does not exist; falls through to RST 18h
RST 18h
Page in the ROM and call it. Inline word parameter is address to call.
RST 20h
Page in the disk BDOS and call it. Inline word parameter is address to call.
Computer model. 0xFF => Hunter 2, 0x7F => SP, others => M-208
RST 28h
RSTC interrupt: Serial port transmit character.
RST 30h
Debug break -- enter builtin monitor.
RSTB interrupt: Serial port commence receive.
RST 38h
Used only by IBM 2780 communucations.
RSTA interrupt: Timer 1 handler.

Notes on BDOS calls

NB: The BDOS function number is not range-checked. Passing an out-of-range function number in C will result in a crash.

Output a byte to the serial port. If the system is configured to echo serial output to the screen, the byte will be echoed.
Get IOBYTE. Only two bits in the IOBYTE have effect: bit 7 is set if console input is coming from the serial port, bit 6 set if console output is going to the serial port.
Ignored. DEMOS only supports one drive.
Ignored. DEMOS only supports user 0.
Clear input buffers.
Built-in time set routine. Prompts for date and time, sets the clock.
Built-in serial port setup routine. Prompts for transmission speed and protocols.
Poll the serial port for input. If input is waiting returns A=0FFh, Zero not set. If not, A=0, Zero set.
Load a file through the serial link.
Modified version of line input (function 0Ah).
Transmit an area of memory down the RS232 link in Intel .HEX format. IX = address, DE = length.
Read an Intel .HEX file from the RS232 link into memory.
Read a character from the keyboard without echoing it to the screen.
Read the real-time clock (if it has been set).
Load BDOS and run overlay. DE points to the overlay FCB; the first byte is the overlay number. Overlays are 1536 bytes long and load at E72Bh; the first byte of the overlay is also its number.
Used in IBM-2780, Systime and ACK/NACK serial configurations.
Append the character in E to the serial port output queue, and start the timer for transmission.
Call the vector at FABCh.
Read a line from the serial port into buffer at F403h. Ends at 96 bytes, or when a CR is received, whichever is first.
Get serial transmit buffer status. If buffer is full, returns A=0, Zero set; otherwise A=0FFh, Zero reset.
Display serial communications error.

John Elliott 16 February 2014