Please enable JavaScript to display the menu.

Unpublished BIOS Functions and I/O ports of FM TOWNS

[ I am writing this to preserve historic Japanese computer called FM TOWNS.  Please feel free to copy this information to anywhere. ]




3C32H 起動ドライブ (Read/Write)

Bit0: ?
Bit1: ?
Bit2: Boot Drive Number LSB (0=A drive)
Bit3: Boot Drive Number
Bit4: Boot Drive Number
Bit5: Boot Drive Number
Bit6: Boot Drive Number MSB
Bit7: ?

Bit2 to Bit6 is the boot drive.  By writing a different drive number to this I/O, FM TOWNS will believe the system started from the number in here.

Bit0, Bit1, and Bit7 seem to have a meaning.  Therefore, when modifying, the program must make sure it first read the byte, and the only update bit2 to bit6.


3A34H, 3A36H デバッグモード? (Read/Write)

If 'R' is written to 3A34H and 'A' is written to 3A36H, SYSINIT.EXE shows progress and debugging information.  It is unknown if other processes are referring to these I/O addresses.



AH=0FDH         Command Code.
AL=0BxH         Device Id.  x is SCSI ID.
CX=0000H        LUN.  Must be zero.
DS:[DI-]        Data Return Buffer Pointer and SCSI Command Information.

DS:[DI]         Data Return Buffer Address
DS:[DI+2]       Data Return Buffer Segment
DS:[DI+4...15]  SCSI Command Up to 12 bytes.

CH!=0           Could not communicate with the SCSI device.
CH=0            Successfully communicated with the SCSI device.  AH=STATUS,  AL=MESSAGE   

CDSD.SYS source code indicates that you need to use this unpublished function to access SCSI CD-ROM drive unless you want to directly beat I/O ports.  Input parameter x of AL=0BxH is SCSI ID.

SCSI command can be as long as 12 bytes.  Actually how many bytes is defined according to the high 3 bits of the command.  ( If high 3 bits are 000 the command takes 6 bytes, 001 10 bytes, 101 12 bytes.

The data reading from the command will be stored from address in WORD PTR DS:[DI], segment in WORD PTR DS:[DI+2].

The result of the command will be returned in CH, AH, and AL.  There are two possibilites:

CH=0, AH=DS:[051AH], AL=DS:[051BH], CH=0


CH=1 or 2, AH undefined, AL undefined.

The return values are set in the following places (addresses are physical addresses.)

Successfully sent an started a SCSI comman?
    557f:    8a 26 1a 05              mov    0x51a,%ah
    5583:    a0 1b 05                 mov    0x51b,%al
    5586:    b5 00                    mov    $0x0,%ch

Probably the SCSI device does not become READY within timeout period:
    In Sub-routine at 561b called from Sub-routine at 54E7
    564b: b5 01 mov $0x1,%ch

Probably the SCSI device does not become BUSY within timeout period:  (Called from 5553->)
    In Sub-routine at 5663 called from Sub-routine at 5553 called from Sub-routine at 54E7
    5701: b5 02 mov $0x2,%ch

It appears that CH=2 means the command was sent but no response, and CH=1 means failed to send the command.

I also found a location where commands are sent, and status bytes are received.

CS=0421H, IP=146BH
    567b:    8b 1e 1c 05              mov    0x51c,%bx  0x051c=Counter.
    567f:    8b 14                    mov    (%si),%dx   -> AH=0FDH, SCSI Disk -> DX=0x0C30 -> IO[0x0C30]=SCSI Data Register  RedBook(*) pp. 262
    5681:    1e                       push   %ds
    5682:    8b 3e 12 05              mov    0x512,%di
    5686:    8e 1e 14 05              mov    0x514,%ds
    568a:    8a 01                    mov    (%bx,%di),%al
    568c:    ee                       out    %al,(%dx)
    568d:    1f                       pop    %ds
    568e:    ff 06 1c 05              incw   0x51c
    5692:    c3                       ret  
    (*) Red Book for FM TOWNS users means "FM TOWNS Technical Guidebook"  
CS=0421H, IP=1483H
    5693:    8b 14                    mov    (%si),%dx   DS:[SI]=0C30H, SCSI I/F Data Register
    5695:    ec                       in     (%dx),%al
    5696:    a2 1a 05                 mov    %al,0x51a
    5699:    c3                       ret    
CS=0421H, IP=148AH
    569a:    8b 14                    mov    (%si),%dx   DS:[SI]=0C30H, SCSI I/F Data Register
    569c:    ec                       in     (%dx),%al
    569d:    a2 1b 05                 mov    %al,0x51b
    56a0:    c3                       ret    

These procedures are called from:

Jump Table
    571d:   1491  <- Vector for NOP                           0 0 0 -> Data Out Phase: Data
    571f:   146b  <- Vector for CMD write                     0 0 1 -> Command Phase: Command
    5721:   1491  <- Vector for NOP                           0 1 0 -> Reserved
    5723:   1491  <- Vector for NOP                           0 1 1 -> Message Out Phase: Message
    5725:   1491  <- Vector for NOP                           1 0 0 -> Data In Phase: Data
    5727:   1483  <- Vector for Read and store in [0x51a]     1 0 1 -> Status Phase: Status
    5729:   1491  <- Vector for NOP                           1 1 0 -> Reserved
    572b:   148a  <- Vector for Read and store in [0x51b]     1 1 1 -> Message-In Phase: Message

CS=0x421 -> IP=151D
Vector to this address is found at Physcai address 0000d990 1D 15 21 04 (DS:[0x520])
    572d:    b8 47 0d                  mov    $0xd47,%ax
    572f:    8e d8                     mov    %ax,%ds
    5732:    be 39 05                 mov    $0x539,%si
    5735:    8b 54 02                 mov    0x2(%si),%dx     DX=0x0C32  SCSI Status Register
    5738:    ec                       in     (%dx),%al
    5739:    a8 80                    test   $0x80,%al        Transfer Request? REQ (Active High)
    573b:    74 0d                    je     0x574a           Jump if no request.
    573d:    24 70                    and    $0x70,%al
    573f:    c0 e8 03                 shr    $0x3,%al         After shift AL Bit2=(0:Output 1:Input), Bit1=(0:Data 1:MSG), Bit0=(0:Data 1:Control)
    5742:    98                       cbtw   
    5743:    8b d8                    mov    %ax,%bx
    5745:    2e ff 97 0d 15           call   *%cs:0x150d(%bx)    CS:[150DH]=4210H+150DH=571DH(Jump Table)
    574a:    cb                       lret   <- Returninb by lret, implies that it is called from FM-R BIOS

The procedure must be registered as a interrupt handler somewhere.  The interrupt vector was found at physical address D990H.  The interrupt is registered at:

     fbe4:    bf 1e 05                 mov    $0x51e,%di
    fbe7:    b2 08                    mov    $0x8,%dl
    fbe9:    b4 00                    mov    $0x0,%ah
    fbeb:    cd ae                    int    $0xae

If DS=0D47H, DS:[051EH] is physical address D98EH.  The interrupt vector registration takes interrupt handler at [DI+2], which makes it D990H.  Perfect match!

Finally, I was able to conclude that the return value of INT 93H AH=0FDH AL=0BxH means AH=DS:[051AH]=SCSI STATUS, AL=DS:[051BH]=SCSI MESSAGE.  The meaning of the STATUS is according to

(STATUS&0x3E)==0:  Good
(STATUS&0x3E)==2:  Check Condition
(STATUS&0x3E)==4:  Condition Met
(STATUS&0x3E)==8:  Busy
(STATUS&0x3E)==16: Intermediate
(STATUS&0x3E)==20: Intermediate Condition Met
(STATUS&0x3E)==24: Reservation Conflict
(STATUS&0x3E)==34: Command Terminated
(STATUS&0x3E)==40: Queue Full



FM TOWNS CD-ROM BIOS INT 93H, AH=0EH, AL=0C0H Test Unit Ready before File Access

FM TOWNS CD-ROM BIOS INT 93H AH=0EH is basically Test Unit Ready, which checks if the media is in the CD-ROM drive and ready to read/write.  But, if the drive is ready and if a flag DS:[564H] is non-zero, it clears the flag and returns Meida Changed Error.

Why is this function necessary?  SCSI CD-ROM Drive has a command Test Unit Ready.  If a CD media is changed after the last SCSI function before Test Unit Ready, it returns Media Changed error.  The computer can tell if the CD media changed by looking at the return value.

But, the things gets complicated if something is cached.  A CD-ROM is slower than a hard disk drive.  Especially moving head to a specific location takes much longer.  It is inefficient to move head to the inner-most position to read directory information every time accessing a file.  Rather, it is faster to cache directory information and not-updating until the media is changed.

But, let's say you start a CD-Player application.  Then you change a media.  Then start playing.  And then go back to the GUI or command shell.  Since CD-Player application already sensed Media Changed Error, the computer won't get a Media Changed Error when it access a file or directory next time.  As a result, even though the CD has changed, the computer does not think it has changed, and show directory of the previous disc.

To prevent this problem, TOWNS_CD driver uses an unpublished function 0EH.  Any CD-ROM BIOS function, when it detects media change, sets a flag DS:[564H] to 1.  TOWNS_CD driver always calls function 0EH before accessing a file or directory.  If the CD drive is Ready and flag DS:[564H] is non-zero, function 0EH clears the flag and returns Media Changed Error.  If the drive is not ready, it just clears DS:[564H] and returns whatever error received from the drive.

This way Function 0EH will tell media change since last file access.


FM TOWNS CD-ROM BIOS INT 93H, AH=0FH, AL=0C0H Direct Access to Internal Flags

Input: CL=5, 1, 2, 4, 8, or 16

Function 0FH is used for directly access internal flags of the CD-ROM BIOS.  I have never observed this function request except CL=5.  This function may be added for debugging purpose and left unchanged in the release version.  I have found five flags in the CD-ROM BIOS:

DS:[564H]  Media Changed Error Cache (See AH=0EH)
DS:[565H]  Set to 1 upon error, reset, or media change, but nothing is referring to it?
DS:[566H]  Checked and cleared at the beginning of CS:[0C16H](Physical 6906H)
DS:[567H]  Set to 1 upon error, reset, or media change, but nothing is referring to it?
DS:[568H]  PAUSE flag (1:Paused  2:Unpaused)

I have figured the meaning of 564H and 568H.  Others seem to have a similar meaning as 564H, but cannot really find the functionality.  Function 0FH is for directly accessing these flags.  If I write it like C++:

int CDROM_BIOS_INT93H_0FH(unsigned char CL,unsigned char &DL)
    case 5:
        return NO_ERROR;
    case 0:
        DL=flag_0564H;  // Media_Changed_Cache
        return NO_ERROR;
    case 1:
        flag_0564H=DL;  // Media_Changed_Cache
        return NO_ERROR;
    case 2:
        return NO_ERROR;
    case 4:
        return NO_ERROR;
    case 8:
        DL=flag_0568H; // Pause flag
        return NO_ERROR;
    case 16:
        flag_0568H=DL; // Pause flag
        return NO_ERROR;
        return PARAMETER_ERROR;

CL==1 case suggests that 564H and 566H are similar.  But, at this time I cannot really tell the exact meaning.



I have never seen this function actually used.  Maybe not used after all.  This function returns PARAMETER ERROR if CH!=0 || DL!=1.  If CH==0 && DL==1, it clears the parameter buffer, but does not issue any command, and returns ABNORMAL TERMINATION ERROR.  It does nothing useful.

     5a2d:    b4 80                    mov    $0x80,%ah
    5a2f:    b9 02 00                 mov    $0x2,%cx
    5a32:    8a 46 07                 mov    0x7(%bp),%al  AL=Incoming_CH
    5a35:    3c 00                    cmp    $0x0,%al
    5a37:    75 16                    jne    0x5a4f
    5a39:    8a 46 08                 mov    0x8(%bp),%al  AL=Incoming_DL
    5a3c:    3c 01                    cmp    $0x1,%al
    5a3e:    77 0f                    ja     0x5a4f
    5a40:    e8 8e 0c                 call   0x66d1        Clear 8 bytes from 57f (parameter buffer) Registers Preserved
    5a43:    3c 01                    cmp    $0x1,%al      AL is already checked.  AL==1
    5a45:    75 06                    jne    0x5a4d        Therefore never jumps.
    5a47:    b4 80                    mov    $0x80,%ah
    5a49:    b9 08 00                 mov    $0x8,%cx      Abnormal Termination
    5a4c:    c3                       ret    
    5a4d:    b4 00                    mov    $0x0,%ah
    5a4f:    c3                       ret    



This function (11H) is called before power off or reset.  But, it is not doing anything.

CD-ROM BIOS INT 93H AL=C0H, AH=11H Unpublished
5a50: b4 00 mov $0x0,%ah
5a52: c3 ret


FM TOWNS CD-ROM BIOS INT 93H, AH=57H, AL=0C0H Set CD-ROM Electric Volume

Input: DS:[DI]   Electric Volume Buffer
    DS:[DI]    0
    DS:[DI+1]  Left Channel Electric Volume 0 to 255
    DS:[DI+2]  1
    DS:[DI+3]  Right Channel Electric Volume 0 to 255
    DS:[DI+4]  2
    DS:[DI+5]  Microphone Electric Volume (Probably)
    DS:[DI+6]  3
    DS:[DI+7]  Modem Electric Volume (Probably)

This function sets electric volume of CD-ROM.  Electric Volume should be written in the 8-byte buffer starting at DS:[DI].  However, this function only reads DS:[DI+1] and DS:[DI+3] and other values are ignored.

This function may return Media Changed Error or Drive Not Ready Error.


FM TOWNS CD-ROM BIOS INT 93H, AH=58H, AL=0C0H Read Electric Volume

Output: DS:[DI]   Electric Volume Buffer
    DS:[DI]    0
    DS:[DI+1]  Left Channel Electric Volume 0 to 255
    DS:[DI+2]  1
    DS:[DI+3]  Right Channel Electric Volume 0 to 255
    DS:[DI+4]  2
    DS:[DI+5]  Microphone Electric Volume (Probably)
    DS:[DI+6]  3
    DS:[DI+7]  Modem Electric Volume (Probably)

It returns the electric volume settings.  Although this is a CD-ROM BIOS functions, it ends up returning also microphone and modem electric volume information.

Electric volume values are written in an 8-byte buffer from DS:[738H].  In CD-ROM BIOS of Towns OS V2.1, DS=0D47H.  DS:[0738H] is physical address DBA8H.

This function may return Media Changed Error or Drive Not Ready Error.



  When DL=0:
    Towns OS V1.1   CL:Third parameter to the command 3FH
    Towns OS V2.1   BL:Third parameter to the command 3FH

This function (61H) is actually used from TOWNS_CD driver.  But the meaning is unknown.  Interestingly, the third parameter to the command is taken from CL in Towns OS V1.1, but from BL in Towns OS V2.1.  If I rewrite in C++ style, it works like this:

int CDROM_BIOS_INT93H_61H(unsigned char DL,unsigned char &BL)
        return IssueCommand(0x3f,{0x02,0x00,0x0d,BL,0,0,0,0})
    else if(DL==1)
        return NO_ERROR;
    else if(DL==0x10)
        return NO_ERROR;
    else if(DL==0x11)
        return NO_ERROR;

Linux for FM TOWNS source (towns_cd.h) gives some hints on internal CD-ROM commands.  But, 3FH (Return State Request Flag + 1FH) is marked as unknown (CDxxx).  In YSSCSICD.SYS, it pretends there is always no error for this command.

Difference between Towns OS V1.1 and V2.1

Towns OS has two major versions, V1.1 and V2.1.  V2.1 has more sophisticated GUI than V1.1, and V2.1L31 added cooperative multi tasking.  V2.1 also has many internal differences from V1.1.  I summarize what I discovered so far.

UMB Usage

Towns OS is based on MS-DOS, probably V3.1.  Towns OS V1.1 keeps everything DOS-related below 640KB, and UMB (640KB to 1MB) area is unused.  Towns OS V2.1 apparently uses some space in the UMB.  I found it while reading disassembly of Galaxy Force II to understand why it didn't start from V2.1.  Towns OS V2.1 crashes if the memory area FC000 to FFFFF is destroyed.  Towns OS V1.1 is unaffected.

DOS Extender

DOS Extender (RUN386.EXE) that comes with Towns OS V2.1 recognizes and avoids used UMB area.  RUN386.EXE of Towns OS V1.1 is not aware of UMB usage.  Therefore, if you start a program on Towns OS V2.1 using RUN386.EXE of V1.1, it might destroy critical UMB area, and the whole system may crash.

Boot Drive

Towns OS V1.1 basically keeps the boot drive as the current drive all the time.  I first thought "Default Drive" of the DOS is different from the current drive.  But later I figured these are the same thing.

Towns OS V2.1 remembers the boot drive in unpublished I/O 3C32H.  Probably the boot loader writes to this I/O.  It can be written after the system starts.  YSSCSICD.SYS modifies this value to let CD-ROM based Towns OS V2.1 think it is started from the CD-ROM drive.


TBIOS or Towns BIOS of Towns OS V1.1 is not aware of the newer versions of FM TOWNS hardware, namely faster models.  Therefore, if you start Towns OS V1.1 on 486DX FM TOWNS in the FAST mode, mouse driver cannot scan mouse movement correctly, and the cursor will jump around.

The workaround for this is to copy TBIOS.SYS and TBIOS.BIN of Towns OS V2.1 L20 (Probably L10 works, too) to the Towns OS V1.1 boot drive.  As far as I experimented with my Towns II MX (486DX), Towns OS V1.1 with V2.1L20 TBIOS runs with no problem and no jumping mouse cursors in the FAST mode.

Comments are welcome.  Send E-Mail to: 

Back to