/*
** environment.txt
**
** Copyright (c) 1997, W. Sheldon Simms III
*/

This file describes the computer environment emulated by the program em65.
That is, it describes what a 65c02 program running on the emulator sees
and how it can access the virtual devices provided by em65. Other files
document different aspects of the em65 emulator as follows:

"65c02.txt"          : how to write 65c02 programs that em65 can execute
"debugger.txt"       : how to use em65's built-in debugger
"implementation.txt" : how em65 is implemented

Please send suggestions, corrections to sheldon@atlcom.net

This file is current with em65 version 0.5

--------------------------------------------------------------------------

*** Introduction ***

Em65 emulates a simple interrupt-driven 65c02 computer. It does not emulate
any real computer system - it is designed to be much easier to control
from 65c02 code than any real computer system.

The address space from $EC00 to $EFFF is generally considered I/O space.
In the current version of em65, only $EC00 to $ECFF is actually used as
such, and $ED00 to $EFFF can be used as regular RAM. However I advise that
this space not be used by 65c02 programs as RAM because future versions
of em65 may claim it for implementation of new virtual devices or for other
purposes.

*** Terminal ***

The virtual terminal device is located at locations $EC00 and $EC01. $EC00
is an I/O port and $EC01 is an interrupt flag. A 65c02 program may write
to $EC00 at any time to print a character to the virtual terminal. For
example, the sequence:

              lda  #'A'         ;get the character 'A'
              sta  $EC00        ;print it on the virtual terminal

will print an A at the current cursor location of the virtual terminal.

(Note that the em65 program multiplexes the real physical terminal between
the virtual terminal device and the debugger screen, so writing to $EC00
will not actually put a character on the screen if the debugger is active)

Getting a typed character from the virtual terminal is a little more
complicated. It is done by reading from location $EC00, but there will
not be a valid character there to read unless a key has actually been
pressed. The way a 65c02 program knows that a key has been pressed, and
that a valid character is available at $EC00 is to wait for an IRQ
interrupt from the virtual terminal device. When the debugger is switched
out and a key is pressed, the virtual terminal will signal an interrupt.
It is up to the 65c02 program to handle the interrupt, determine that the
terminal caused the interrupt, and then read $EC00.

The way that the 65c02 program knows that the terminal generated the
interrupt is by reading location $EC01. If the terminal has generated an
interrupt since the last time $EC01 was read, then a read of $EC01 will
return the value $80. Reading $EC01 at any other time will return the
value $00 So for example, a 65c02 program can get characters from the
terminal with code like this:

irq_handler   lda  $EC01        ;see if the terminal is the interrupter
              bpl  not_terminal ;if not, find out who it was
              lda  $EC00        ;it was the terminal, get the character
              sta  $EC00        ;echo the character
              sta  save_char    ;save the character
              bra  irq_end      ;quit the interrupt handler
not_terminal  ...               ;do whatever
irq_end       rti

assuming that locations $FFFE/$FFFF, the IRQ vector, refer to the address
of irq_handler.

*** Disk ***

The virtual disk is represented as a file, named em65_disk, which is
created and kept in the working directory of the em65 program. The
virtual disk is an emulated simple DMA device - that is, the virtual disk
device will read and write data from the emulated disk directly into the
65c02's memory. This makes is quite easy to read and write the virtual
disk from 65c02 programs.

The virtual disk is located at locations $EC10 and $EC11. Location $EC10
is a command port. Writing to $EC10 sends commands to the virtual disk
device telling it what service to perform and how to perform it. The disk
controller signifies that it has finished an operation by interrupting
the emulated 65c02. Location $EC11 is an interrupt flag which operates
exactly the same as $EC01 does for the terminal.

There are three commands which can be written to $EC10, each of which takes
arguments, which must be written to $EC10 after the command byte. The three
commands are:

$01 - Get disk information
$02 - Read block from disk
$03 - Write block to disk

The argument for command $01 is a 16 bit memory address, written low byte
first to $EC00. The disk controller will then write the disk information
to the specified address.

The arguments for commands $02 and $03 are a 16 bit block number followed
by a 16 bit memory address. The virtual disk controller will then either
read a block from the disk into the 65c02's memory starting at the
specified address, or it will write the memory starting at the address to
the specified block on the disk.

Example of get disk info:

info_buf      .dsb 16           ;allocate 16 bytes for disk info

              ...

              lda  #$01
              sta  $EC10        ;command "get disk info"
              lda  #<info_buf   ;store the info at info_buf
              sta  $EC10        ;this is the low byte
              lda  #>info_buf
              sta  $EC10        ;this is the high byte
              
/* now the disk is busy and will interrupt when it is done */

              ...


Example of read/write block:

my_block      =    $4242
disk_buffer   .dsb 512

              ...

              lda  #$02         ;command for "read block"
              sta  $EC10
              lda  #<my_block
              sta  $EC10
              lda  #>my_block   ;read block $4242 from the disk
              sta  $EC10
              lda  #<disk_buf
              sta  $EC10
              lda  #>disk_buf   ;have the disk store it in disk_buffer
              sta  #EC10
              
/* now the disk is busy and will interrupt when it is done */

              ...


The virtual disk device is not formatted in any way. It is access merely
as a sequence of 512 byte blocks. Obviously, a 65c02 program could impose
any format upon it that it wished.

The disk information written to memory by the disk controller in response
to command byte $01 is mostly bogus - just to make the disk controller
seem more like a real disk. It is 16 bytes in this format:

1 byte  : number of heads
1 byte  : number of sectors per track
2 bytes : number of bytes per sector
2 bytes : number of cylinders
2 bytes : number of physical partitions
4 bytes : number of disk reads
4 bytes : number of disk writes

Only the last three entries are meaningful. The number of reads and writes
is maintained by the virtual disk controller and the real numbers are
returned by the get disk info command. The number of physical partitions
is a field that will allow the disk to be extended to more than 65536 blocks
in the future, if necessary. For now it will always be 1. All of the other
fields are bogus, except for the fact that they must add up to produce a
virtual disk of 32mb in size. Actually, they can be thought of as describing
a physical partition of 32mb in size, but since the virtual disk only has
one physical partition for now, it is essentially the same thing.

Lastly, it may seem that a 65c02 program might get confused about what it
has written to $EC00 and therefore not know of the disk controller is ready
to accept a full command sequence. For example, if the disk controller has
already received a partial command, but the 65c02 program doesn't know that,
and tries to send a full command sequence, the results are undefined and
could be disastrous. The disk controller might think it was told to write
over the 65c02 program, for example. To allow 65c02 programs to avoid this,
reading $EC00 will reset the virtual disk controller, so that a full command
sequence written afterward will be guaranteed to work properly.

Here is a code sample handing the disk and terminal:

/***** disk operations *****/

reset_disk    lda  $EC10
              rts

read_disk     inc   disk_busy    ;claim the disk
             ...
/* do command sequence to read disk */
             ...
rdisk_wait    lda   disk_busy    ;wait for the disk to finish
              bne   rdisk_wait

write_disk    inc   disk_busy    ;claim the disk
             ...
/* do command sequence to write disk */
             ...
wdisk_wait    lda   disk_busy    ;wait for the disk to finish
              bne   wdisk_wait

disk_info     inc   disk_busy    ;claim the disk
             ...
/* do command sequence to get disk info */
             ...
dinfo_wait    lda   disk_busy    ;wait for the disk to finish
              bne   dinfo_wait

/***** disk I/O data *****/

disk_busy     .byt  0            ;allocate a byte initialized to 0
disk_buffer   .dsb  512          ;allocate 512 uninitialized bytes

/***** terminal operations *****/

put_char      sta   $E000
              rts

get_char      lda   char_ready   ;see if a new character has arrived
              beq   no_char      ;if not, return with carry set
              stz   char_ready   ;if so, clear flag
              lda   new_char     ;get the char
              clc                ;clear the carry
              rts                ;and return
no_char       sec
              rts

/***** interrupt handler writes, get_char reads *****/

char_ready    .dsb  1
new_char      .dsb  1

/***** Interrupt handler *****/

irq_handler   pha                ;save registers
              ...
       /* check for BRK  */
              ...
              lda   $EC01        ;was irq from terminal?
              bmi   irq_term
              lda   $EC11        ;was irq from disk?
              bmi   irq_disk
              ...
 /* check other interrupt sources, if any */
              ...
irq_disk      stz   disk_busy    ;clear disk_busy flag
              bra   irq_done

irq_term      lda   $EC00        ;get the character
              sta   new_char     ;save it
              inc   char_ready   ;set flag to say it arrived

irq_done      rti


===============================================================================
