post

Emma by L.J. Technical Systems

The EMMA and EMMA II SBC’s were produced by LJ Electronics Ltd, Norwich, a company established in 1979 to market training technology to colleges, schools and universities. The company behind LG Electronics still exists but is now known as LJ Create.

Here you find information on:
EMMA
EMMA II
EMMA emulator by Colin Grey

Emma

Emma, full name MS1 Emma Microcomputer, is a rudimentary 8-bit 6502 processor board, quite similar to the KIM-1 family, with attached hex-input keypad and 7-segment display that was marketed by LJ Electronics in the early eighties. Despite its mere 1K of RAM and 2K monitor in EPROM, it was deemed suitable for driving the Atlas arm. In addition to the processor itself, the Emma range,L.J. Electronics Basic Microprocessor Application System MA02/1, included a large number of sensing, simulation and input-output boards that Emma could be hand-coded to interact with. There was even a mini matrix printer for hard-copy output.
The images below show the Emma processor, keypad and display unit, plus many of the available add-on boards. More complex expansion options included an Eprom programmer and an A4 plotting table. Further capability could be added in the form of the Visa Expansion System – shown above connected to Emma. This added a video interface, 8K of RAM, 8K of BASIC, 6K of assembler and monitor and a full QWERTY keyboard.
EMMA uses 6502 with 1 MHz crystal, an INS8154 + 74145 RAM I/O IC for 24 keys scanning and 8 seven segment displays, a 6522 VIA for general purpose I/O, 2x 2114 SRAM and a 2716 for the monitor program. A group of CMOS CD4XXX IC’s delivers a Cassette interface

Any additional information, like software, is appreciated!

EMMA manual in PDF format. L.J. Electronics Basic Microprocessor Application System MA02/1, consisting of the MS1 Emma Microcomputer and numerous components, as listed in the MA-02 manual.

Hardware Manual for the EMMA  – 46 pages plus large appendices If ou have it, plaese help me scan and publish it! I only have three pages as seen in a recent ebay listing:




Emma computer with Visa upgrade, controlling an early Atlas robot arm

Emma II

Emma II was a more compact and complete successor of the Emma. Quite similar, but the keyboard encoding is now done with a 6821 PIA. A VIA 6522 is also present and the usual hex keybaord and seven segment display.

Emma II is well documented (thanks John Evans)

EMMA II Technical manual in PDF format, including schematics and monitor source, 115 pages.

 

 

 

 

 

 

EMMA II User manual in PDF format, 109 pages full of experiments.

Token-ring with EMMA II
Running lights with EMMA II
Flashing text with EMMA II
EMMA II EPROM bin
EMMA II EPROM hex
EMMA II EPROM memory expansion bin
EMMA II EPROM memory expansion hex

Memory Expansion module with RAM ROM and EPROM Programmer (Photo John Evans)

EMMA emulator by Colin Grey

This is a 6502 simulator. It is loosely based on the LJ Technical Systems EMMA board. Although quite useful it is not a full implementation. Download here.

post

John Bell Engineering SBC’s

John Bell Engineering was a small firm producing microprocessor many boards between 1980 and 1990.
The early boards were 6502 and Z80 SBC’s, later boards were for the Apple II, and the last ones for the IBM PC bus.

I present here, what I have found, of three boards in the spirit of http://retro.6502.nl. Any additional information, like software, is appreciated!

82-300 Three IC’s SBC 6502, 6532 2716
80-153 SBC 6522, 6502

82-300 (circuit drawn by Mike K8LH)

One of the smallest 6502 based computers possible with the technology of the 1980ties!

Any additional information, like software, is appreciated!

Three IC’s SBS 6502 CPU, 6532 RIOT, EPROM  2532 or 2716, expansion connector, RC oscillator, one transistor decoder


80-153 6502 micro

Five IC’s SBS 6502, 6522 VIA,  2716 EPROM, 2x 2114 SRAM, 74LS10 decoder

There was a revision, connecting phi2 to pin 11 of the ‘LS10, “to improve memory response”

(Clock modification with crystal,  Don Sawyer)

There was a revision, connecting phi2 to pin 11 of the ‘LS10, “to improve memory response”, so the wire is soldered!

There was a revision, connecting phi2 to pin 11 of the ‘LS10, “to improve memory response”


KIM Clone Corsham Technologies

A new KIM CLone kit is coming!

Bob Applegate is filling in the gap left by Vince Briel, with his own design.

  • 6502 for now, but will probably ship with a 65C02 (extra instructions).
  • Fully KIM-1 software compatible.
  • 5K memory from 0000 to 13FF, then another 56K from 2000 to FFF7.
  • The top 8K of RAM can be turned off and 8K of EEPROM can be enabled (socket on board for the EEPROM).
  • Has an 8V power plug and on/off switch.
  • No cassette interface.
  • USB serial board.  Ie, plug a USB cable between your computer and the KIM clone to use the TTY port.
  • Has connector to plug in our SD System.  Store/retrieve programs from a micro SD card.

KIM Clone Progress

 

post

EhBASIC bug Ibuffs location

Bug in EhBASIC 2.22 Daryl Rictor et al (from a discusson on 6502.org)

The following works:

; Ibuffs can now be anywhere in RAM, ensure that the max length is < $80
IRQ_vec   = VEC_SV+2      ; IRQ code vector
Ibuffs      = IRQ_vec+$14

Whereas the following doesn’t:

; Ibuffs can now be anywhere in RAM, ensure that the max length is < $80
Ibuffs      = $0400
Ibuffe      = Ibuffs + $47    ; end of input buffer

Of course be careful to make sure that Ram_base is quite faraway from Ibuffs.

So, Ibuffs can be anywhere in RAM that’s not $0400??
Any reason $0400 is magical?
Nope. $0400 is an example. $0500 gives you the same result: negative.
As long as Ibuffs is in the same page as ccflag and friends, it works. And ccflag can be at $0400.

Now, if you reference Ibuffs in relation to IRQ_vec (copy it over from min_mon.asm), then things work.
Perhaps someone somewhere made some assumptions of the page where Ibuffs need to be e.g. be in the same page as ccflag and friends etc.

The other problem is what I’ve already observed: the EhBASIC binary has to be located in memory higher than Ram_base and Ram_top. Otherwise strange things happen.

With Ibuffs set to $0400 AND Ram_base set to $0500, I did a Cold start and entered this line – 10 PRINT “HELLO WORLD”

I examined the memory at Ram_base and found that the stored line was formatted correctly. However, when I typed LIST, all I got back was HELLO WORLD, as if I had typed RUN. Typing RUN also returned HELLO WORLD. When I ran the SYS command (my added command to return me to my Monitor), it just ignored it and returned to the BASIC input prompt.

Next, I changed Ibuffs to $0401 and reassembled. This time, everything performed as it should.

Next, I set Ram_base to $0600 with Ibuffs set to $0400 and entered the same line. This time, LIST returned nothing. Examining memory at $0600, the stored line is there and is correct. RUN and SYS also return nothing.

Finally, I set Ram_base to $0600 and Ibuffs to $0401 and all worked as it should.
I think I found the problem… and it would explain the issue with Ibuffs and Ram_base being 1 page apart too.

This piece of code is the problem:

LAB_142A
   INY            ; increment pointer
   INY            ; increment pointer (makes it next line pointer high byte)
   STA   Ibuffs,Y      ; save [EOL] (marks [EOT] in immediate mode)
   INY            ; adjust for line copy
   INY            ; adjust for line copy
   INY            ; adjust for line copy
   DEC   Bpntrl      ; allow for increment (change if buffer starts at $xxFF)
   RTS

Previously in the code, Bpntrl gets set to Ibuffs. When the DEC instruction gets executed, Bpntrl get set to $FF. Later in the code, we have this:

LAB_15F6
   JSR   LAB_IGBY      ; increment and scan memory

LAB_15F9
   JSR   LAB_15FF   

The routine LAB_IGBY is located in zero page (to increase speed). it looks like this:

LAB_2CEE
   INC   Bpntrl      ; increment BASIC execute pointer low byte
   BNE   LAB_2CF4      ; branch if no carry
               ; else
   INC   Bpntrh      ; increment BASIC execute pointer high byte

; page 0 initialisation table from $C2
; scan memory

LAB_2CF4
   LDA   $FFFF         ; get byte to scan (addr set by call routine)
   CMP   #TK_ELSE      ; compare with the token for ELSE
   BEQ   LAB_2D05      ; exit if ELSE, not numeric, carry set

   CMP   #':'         ; compare with ":"
   BCS   LAB_2D05      ; exit if >= ":", not numeric, carry set

   CMP   #' '         ; compare with " "
   BEQ   LAB_2CEE      ; if " " go do next

   SEC            ; set carry for SBC
   SBC   #'0'         ; subtract "0"
   SEC            ; set carry for SBC
   SBC   #$D0         ; subtract -"0"
               ; clear carry if byte = "0"-"9"
LAB_2D05
   RTS

The code at LAB_2CEE will INC Bpntrl, which becomes $00 and then INC’s Bpntrh to $05. Now this pointer is pointing to the wrong page. And depending upon what is stored there, different things will happen.

This note is incorrect:

   DEC   Bpntrl      ; allow for increment (change if buffer starts at $xxFF)

It should read (change if buffer starts at $xx00).

My fix would be to add this:

LAB_142A
   INY            ; increment pointer
   INY            ; increment pointer (makes it next line pointer high byte)
   STA   Ibuffs,Y      ; save [EOL] (marks [EOT] in immediate mode)
   INY            ; adjust for line copy
   INY            ; adjust for line copy
   INY            ; adjust for line copy

; add patch for when Ibuffs is $xx00 - Daryl Rictor 
   LDA  Bpntrl         ; test for $00
   BNE  LAB_142P      ; not $00
   DEC   Bpntrh      ; allow for increment when $xx00
LAB_142P
; end of patch
   DEC   Bpntrl      ; allow for increment
   RTS

I have tested this fix and it worked for me.

Daryl

post

Enhanced 740 BASIC Language reference

Enhanced BASIC 740, language reference.

Most of EhBASIC74 is identical in operation to 6502 EhBASIC so this page will just describe the keywords that differ from that version.
Code that doesn’t use the features of one version should run identically on the other.

BASIC Keywords

Here is a list of BASIC keywords that differ from the 6502 version of EhBASIC. They
are only valid when entered in upper case as shown and spaces may not be included
in them. So, for example, TIMER1 is valid but TIMER 1 is not.

A2D CLEAR CNTR0 CNTR1 COUNT D2A1
D2A2 EVENT FLUSH INT0 INT1 INT2
INT3 INT4 IRQ NMI ON OFF
PRES12 PRESX PRESY PULSE PWM RETIRQ
RETNMI RETURN START STOP TIMER TIMER1
TIMER2 TIMERX TIMERY +

Anything in upper case is part of the command/function structure and must be present
Anything in lower case enclosed in < > is to be supplied by the user
Anything enclosed in [ ] is optional
Anything enclosed in { } and separated by | characters are multi choice options
Any items followed by an ellipsis, … , may be repeated any number of times
Any punctuation and symbols, except those above, are part of the structure and must be included

var is a valid variable name
var$ is a valid string variable name
var() is a valid array name
var$() is a valid string array name
expression is any expression returning a result
expression$ is any expression returning a string
result
addr is an unsigned integer in the range +/- 16777215 that will be
wrapped to the range 0 to 65535
b is a byte value 0 to 255
n is an integer in the range 0 to 63999
w is an integer in the range -32768 to 32767
i is a +ve integer value
r is real number
+r is a +ve value real number (0 is considered +ve)
$ is a string literal

BASIC74 Commands

FLUSH

This command, specific to version 1.10 on, clears the input buffer of any pending keystrokes. The buffer is also cleared by CLEAR and RUN.

INTx {ON|OFF|CLEAR}

Changes INTx event handling (where x is one of 0, 1, 2, 3 or 4).
ON
Enables the INTx handling subroutine after it has been turned off.
Note If a INTx event occured while the INTx event handling was disabled then it will be actioned immediately after this command.
OFF
Disables the INTx handling subroutine but does not remove the assignment. INTx events that occur while the handler is disabled will still be flagged for action.

CLEAR
Cleares the INTx handling assignment. After an INTx CLEAR the handling subroutine can only be restarted with an ON INTx command.


INTx {+|-}

Sets the detection edge for INTx (where x is one of 0, 1, 2, 3 or 4). + sets a low to high transition (positive edge) as the trigger, – sets a high to low (negative edge) as the trigger. The default is -. Note that changing the trigger edge when the interrupt is enabled may cause the interrupt to trigger.


CNTRx {ON|OFF|CLEAR}

Changes CNTRx event handling (where x is either 0 or 1).
ON

Enables the CNTRx handling subroutine after it has been turned off.
Note If a CNTRx event occured while the CNTRx event handling was disabled then it will be actioned immediately after this command.

OFF

Disables the CNTRx handling subroutine but does not remove the assignment. INTx events that occur while the handler is disabled will still be flagged for action.

CLEAR

Cleares the CNTRx handling assignment. After a CNTRx CLEAR the handling subroutine can only be restarted with an ON CNTRx command.


CNTRx {+|-}

Sets the detection edge for CNTRx (where x is one of 0 or 1). + sets a low to high transition (positive edge) as the trigger, – sets a high to low (negative edge) as the trigger. The default is -. Note that changing the trigger edge when the interrupt is enabled may cause the interrupt to trigger.


PRESq <b>

Sets the value for prescaller q (where q is one of 12, X or Y).


TIMERp {TIMER|PWM|COUNT|PULSE}

Sets TIMERp timing mode (where p is either X or Y).
TIMER

The timer (X or Y) counts down (f/16)/(1+PRESq)/(1+TIMERp).

PWM

If the corresponding active edge selection bit (CNTR0 for timer X, CNTR1 for timer Y) is “0” (CNTRx -) then the counter counts as in timer mode while the pin is at “1”.
If the corresponding active edge selection bit (CNTR0 for timer X, CNTR1 for timer Y) is “1” (CNTRx +) then the counter counts as in timer mode while the pin is at “0”.
The Prescaller and timer latches are preset to $FF by setting this mode and when the selected event (-ve or +ve edge) happens the prescaller and timer values are copied and available to BASIC using the TIMERp (where p is either X or Y) function.

COUNT

The same as timer mode except pulses on the corresponding counter pin (CNTR0 for timer X, CNTR1 for timer Y) are counted. Selecting this mode also sets the corresponding pin to input mode.

PULSE

As timer mode except when the timer reaches $0000 the counter pin (CNTR0 for timer X, CNTR1 for timer Y) is inverted. Selecting this mode also sets the corresponding pin to output mode.


TIMERp {START|STOP}

Enables or disables counting for TIMERp (where p is either X or Y).


TIMERq {ON|OFF|CLEAR}

Changes TIMERq event handling (where q is one of 1, 2, X or Y).
ON

Enables the TIMERq handling subroutine after it has been turned off.
Note If a TIMERq event occured while the TIMERq event handling was disabled then it will be actioned immediately after this command.

OFF

Disables the TIMERq handling subroutine but does not remove the assignment. TIMERq events that occur while the handler is disabled will still be flagged for action.

CLEAR

Cleares the TIMERq handling assignment. After a TIMERq CLEAR the handling subroutine can only be restarted with an ON TIMERq command.

TIMERq <b>

Sets the value of TIMERq (where q is one of 1, 2, X or Y).


RETIRQ

This command has been removed from EhBASIC74. It’s function is now handled by an enhanced RETURN command.


RETNMI

This command has been removed from EhBASIC74. It’s function is now handled by an enhanced RETURN command.


RETURN

As well as returning program execution to the next statement after the last GOSUB in EhBASIC74 RETURN also restores execution to the next statement after event
subroutine has been executed. RETURN checks the type of event and automatically restores the enabled flag for the interrupt that caused the event.


ON

See INTx {ON|OFF|CLEAR}, CNTRx
{ON|OFF|CLEAR} or TIMERq {ON|OFF|CLEAR}.


OFF

See INTx {ON|OFF|CLEAR}, CNTRx
{ON|OFF|CLEAR} or TIMERq {ON|OFF|CLEAR}.


CLEAR

See INTx {ON|OFF|CLEAR}, CNTRx
{ON|OFF|CLEAR} or TIMERq {ON|OFF|CLEAR}.


ON {IRQ|NMI} <n>

These commands have been removed from EhBASIC74 as there is no IRQ or NMI interrupt on the 740 series microprocessor.


ON INTx <n>

Set up the INTx routine pointers (where x is one of 0, 1, 2, 3 or 4). This sets up the effective GOSUB line that is taken when an interrupt happens. When the effective GOSUB is taken the interrupt, INTx, is turned off. The interrupt is turned back on when normal program flow is resumed by a matched RETURN command.


ON CNTRx <n>

Set up the CNTRx routine pointers (where x is one of 0 or 1). This sets up the effective GOSUB line that is taken when a counter interrupt happens. When the
effective GOSUB is taken the interrupt, CNTRx, is turned off. The interrupt is turned back on when normal program flow is resumed by a matched
RETURN command.


ON TIMERq <n>

Set up the TIMERq routine pointers (where q is one of 1, 2, X or Y). This sets up the effective GOSUB line that is taken when a timer interrupt happens. When the
effective GOSUB is taken the interrupt, TIMERq, is turned off. The interrupt is turned back on when normal program flow is resumed by a RETURN
command.


IRQ {ON|OFF|CLEAR}

This command has been removed from EhBASIC74 as there is no IRQ interrupt on the 740 series microprocessor.


NMI {ON|OFF|CLEAR}

This command has been removed from EhBASIC74 as there is no NMI interrupt on the 740 series microprocessor.


D2Ax <b>

Output byte b as an analog voltage on D-A port x (where x is 1 or 2). Setting either convertor also sets the corresponding pin to analog output mode.

D2Ax {ON|OFF}

Enables or disables the D2Ax output (where x is one of 0 or 1).


+

See INTx {+|-} or CNTRx
{+|-}.


See INTx {+|-} or CNTRx
{+|-}.

BASIC74 Functions

Functions always return a value, be it numeric or string, so are used on the right hand side of the = sign or in commands requiring an
expression e.g. after PRINT, within expressions, or in other functions.


A2D(<b>)

Returns the value of the A-D convertor input b, where b is 0 to 7. Any value for b outside this range will cause a function call error.


EVENT

Returns a boolean value. If any enabled interrupt has occured since EVENT was last examined then EVENT will return -1 else EVENT will return 0


TIMERp

Returns the count value from TIMERp (where p is either X or Y). As this is a count down timer the value returned is first subtracted from zero then returned as if it had been DEEKed from the timer registers. This function is only of any use when TIMERp is being used in PWM mode.

post

Enhanced 740 BASIC

Enhanced BASIC74 is a BASIC interpreter for the 740 microprocessor family. It is constructed to be quick and powerful and includes instructions to facilitate easy low level handling of 740 family hardware devices. It also retains most of the powerful high level instructions from similar BASICs.

EhBASIC74 is derived from EhBASIC for the 6502 and shares much of the code used in that BASIC. However EhBASIC74 also includes enhancements to make it specifically more usefull on the Mitsubishi 740 series of single chip micros. While it was developed on the M38067 it should be possible to run it on most of the 740 family with only minor modifications.

EhBASIC74 is free but not copyright free. For non commercial use there is only one restriction, any derivative work should include, in any binary image distributed, the string “Derived from EhBASIC” and in any distribution that includes human readable files file that includes the above string in a human readable form e.g. not as a comment in an HTML file.

  • Language reference
  • Code examples

19th June 2003.
Confusingly the main version number is now the 3806 specific version number so, while the generic BASIC version is still 1.09, this incarnation is 1.10.This adds an interrupt driven receive buffer and an extra command to help use it. Go see the language reference for details.

18th June 2003.
This is the last version for the SuprDupr and SuprChip boards that will use polled input. The next version will have an interrupt driven Rx buffer for better response. As such version 1.09 will remain available for download here.

AT keyboard interface driver

The SuprDupr has a PS2 style socket for an AT keyboard. However, as it arrived, the data and clock go to the GAL and so were jumped directly to port 7, bits 7 and 6, on the micro. There are also positions for pullup resistors on both the keyboard data and clock lines, these are not needed and were omitted. These details may have changed as this was an early production board.
The software is unfinished and provided as a starting point for anyone who wishes to make their own. As provided the driver provides very reliable basic keyboard handling. During developement it was noticed that the keyboard driver would sometimes hang so some test code was added to monitor the reliability (included in the source but commented out) and it was found that the routine wouldn’t return about once in every 750,000 calls.

This was eventually traced to a faulty keyboard but highlighted the vunerability of the code to spurious responses from the keyboard. As a result the robustness of the code was improved by adding timeouts and the resulting code proved reliable over hundreds of millions of calls.

Download the source here

LCD port driver

The SuprDupr has a port wired to enable use of many inteligent LC displays. The display, pictured right, is an LC7980 type though most of the other graphics and
text types are similarly driven.
One change needed to be made to the SuprDupr to use this display and that was to disconnect the earthy end of the contrast pot from 0v and connect it to the -ve
charge pump output on the MAX232 chip. This is because the greater multiplex on these graphics displays requires a more -ve bias to maintain the viewing angle.

A down side to this is that display contrast is effected by the loading on the RS232 port and use of such things as a RS232 line monitor can have an adverse
effect on the display. Also if the load from the display is to great then the MAX232 charge pump will not start. However this should not be a problem in normal use.
Another change made to the SuprDupr was to wire +12v to the spare pin on the LCD port to provide power for the backlight. This could be connected through a PNP
transistor and controlled by software via one of the port pins.

The software is unfinished and provided as a starting point for anyone who wishes to make their own. It is usable and was used to produce the displays pictured above and below.

Download the source here


8 Bit ISA slot

I wanted a quick way to connect an 8 bit ISA slot, only to access the IO adresse range, to the SuprChip V microcontroller. The circuit I came up with appears on the right.

The address lines A5 to A15 can either be set using jumpers or switches (I used switches) or, if you only need one address range, can be hard wired to the corresponding address.

Any of the five interrupt lines, from IRQ3 to IRQ7, can be selected by the use of a jumper. Again if you will only need one (or none) of these then the desired interrupt can be hard wired.

This circuit could be fairly easily adapted as a memory mapped device by connecting the corresponding address and data lines (possibly through buffers), by driving the IOR and IOW pins with decoded read and write signals, and by connecting the interrupt, via an invertor, to the NMI input on the processor. The RESET line can be driven, again via an invertor, by the processor RESET line or by an output port line. This line is active high.

While experimenting with different network cards I discovered some of them need the +12v supply in addition to the +5v supply. This additional connection is shown in red on the diagram.

As I had a stack of 3Com 3C509B NICs of various types I decided to try to use them. Well it turns out that even with PnP disabled you still need to access the card’s id port on $01xx. To allow this Port 6 bit 6 was connected to the A9 line, so both $03xx and $01xx can be accessed by the software. This additional connection is shown in green on the diagram.

Though the slot can be accessed entirely from EhBASIC it was decided to create two short routines to make accessing the slot faster and easier. To that end one function, to read a byte, and one subroutine, to write a byte, were written.

After initialising the function and subroutine addresses thus ..

..
30 DOKE $44,$EF20 : REM USR(n) = read from IO address $xxnn	
31 rr = $EF00 : REM call address for write to IO address
..

It is a simple matter to write a byte by doing ..

20500 CALL rr,cr,dd : REM set control register			
20505 REM cr is the IO address - $00 to $1F
20510 REM dd is the byte value for that IO address

.. and read a byte by doing ..

10050 by = USR(cr)						
10055 REM cr is the IO address - $00 to $1F
10060 REM the byte value is returned to by

You can download the assembly source for the read and write routines.

post

EhBASIC LOAD and SAVE notes

The quickest and easiest way to save the program is to do this …

Type LIST but not the return ..
Set your terminal program to ASCII capture..
Hit return to start the LIST command..
Once the list is complete stop the ASCII capture.

To load the program back there are two possible, similar, ways.

If your hardware supports hardware handshake properly just select the listing and use ASCII send to transfer it (You’ll need to edit the ‘Ready’ prompt from the end of the listing).

If your hardware doesn’t support hardware handshaking then you’ll need to paste a number of SPACE characters at the start of each line to give the interpreter enough time to tokenise the previous line. The number of spaces depends on two things, how fast your processor is and how fast the serial link is. The faster the processor the fewer spaces, the faster the serial link the more spaces. Otherwise just do as above.

(Alternately edit the NULL command to insert $20’s instead of $00’s into the start of each line and do ..
NULL n
LIST
NULL 0
when you save the list)

If you want to save the program as binary you should save (Smeml) to (Svarl)-1.

If you want to save the program as ASCII you should redirect the character output vector to write to your filesystem and then call LIST. Doing this can also allow you to specify line numbers or ranges as with LIST. The output vector should be restored and the file closed when LIST returns or an error is encountered.

To load a binary program start loading it at (Smeml) and set (Svarl) to the last address + 1 then call LAB_1477 to clear the variables and reset the execution pointer. An easy way to do the first part is by copying (Smeml) to (Svarl) and using (Svarl) as a post incremented save pointer. If there is a chance that the program has been relocated it’s probably a good idea to rebuild the line pointer chain.

To load an ASCII program redirect the character input vector to read from your filesystem and return to the main interpreter loop. The input vector should be restored and the file closed when the file end is reached or an error is encountered.

I prefer ASCII format as it’s far more portable, easier to manipulate and can include direct commands and comment lines that aren’t saved to memory.

Is there an easy way to support a filename in the LOAD and save COMMANDs, and get access to that from my code? Right now I am prompting the user for it.

Call the evaluate following expression routine, LAB_EVEZ, and look for a string by testing if the data type flag, Dtypef, is negative. If it is the string descriptor is on the descriptor stack so don’t forget to pop it off there by calling LAB_22B6 once you’re done with it.

Look at Jeff Trantor’s implemention of LOAD and SAVE for the Apple 1 with a CFFA1 and for the OSI C1P via the serial line.

Daryl Rictor wrote a simple SAVE and LOAD program:

;******** LOAD & SAVE PATCH FOR ENHANCED BASIC ON 65C02 Simulator

psave		
		jsr	pscan
		ldy	#$00
		lda	itempl
		sta	(itempl),y
		iny
		lda	itemph
		sta	(itempl),y
		ldx	smeml
		lda	smemh
		jsr	print2byte
		jsr	print_cr
		sec
		lda	itempl
		sbc	smeml
		tax
		lda	itemph
		sbc	smemh
		jsr	print2byte
		jsr	print_cr
		rts

pload		
		jsr	pscan
		lda	itempl
		sta	svarl
		sta	sarryl
		sta	earryl
		lda	itemph
		sta	svarh
		sta	sarryh
		sta	earryh
		JMP   LAB_1319		
pscan
		lda	smeml
      	sta	itempl
      	lda	smemh
      	sta	itemph
pscan1	ldy   #$00
		lda   (itempl),y
		bne   pscan2
		iny   
		lda   (itempl),y
		bne   pscan2
		clc
		lda   #$02
		adc   itempl
		sta	itempl
		lda	#$00
		adc	itemph
		sta	itemph
		rts
pscan2	ldy   #$00
		lda	(itempl),y
		tax
		iny
		lda	(itempl),y
		sta	itemph
		stx	itempl
		bra	pscan1

Another version of this:

;******** LOAD & SAVE PATCH FOR ENHANCED BASIC ON SBC-3
; Daryl Rictor, Feb 2009
;

.include dos.lbl			; get DiskOS pointers
;input		; sbc scanned input
;output		; text output
;DiskOS		; diskos entry
;SaveF		; save program file from BASIC
;LoadF		; load program file from BASIC

;ibuffs+1 ($420) contains the name, start addr, end address

psave		ldx	#$00
		lda	ibuffs+1
		stz	ibuffs+1	; this fixes syntax error in ehbasic
		bne	psave15
		rts
psave1		lda	ibuffs+1,x
		stz	ibuffs+1,x	; this fixes syntax error in ehbasic
		beq	psave3
psave15		cmp	#$b9
		bne	psave2
		lda	#"/"
psave2		sta	buffer+2,x
		inx
		cpx	#$46
		bne	psave1
		rts			; entry too long, abort
psave3		lda	#","
		sta	buffer+2,x
		inx
		phx
		jsr	pscan		; Find end of program
		plx
		lda	smemh
		jsr	ToHex
		lda	smeml
		jsr	ToHex
		lda	#","
		sta	buffer+2,x
		inx
		lda	itemph
		jsr	ToHex
		lda	itempl
		jsr	ToHex
		lda	#$00
		sta	buffer+2,x
		jsr	SaveF		; save file to IDE (in DiskOS)
		rts

pload		ldx	#$00
		lda	ibuffs+1
		stz	ibuffs+1	; this fixes syntax error in ehbasic
		bne	pload15
		rts
pload1		lda	ibuffs+1,x
		beq	pload3
pload15		cmp	#$b9
		bne	pload2
		lda	#"/"
pload2		sta	buffer+2,x
		inx
		cpx	#$47
		bne	pload1
		rts			; entry too long, abort
pload3		lda	#$00
		sta	buffer+2,x
		jsr	Loadf		; load file from IDE (in DiskOS)
		jsr	pscan
		lda	itempl
		sta	svarl
		sta	sarryl
		sta	earryl
		lda	itemph
		sta	svarh
		sta	sarryh
		sta	earryh
		JMP   LAB_1319		

pscan
		lda	smeml
	      	sta	itempl
      		lda	smemh
	      	sta	itemph
pscan1		ldy   #$00
		lda   (itempl),y
		bne   pscan2
		iny   
		lda   (itempl),y
		bne   pscan2
		clc
		lda   #$02
		adc   itempl
		sta	itempl
		lda	#$00
		adc	itemph
		sta	itemph
		rts
pscan2		ldy   #$00
		lda	(itempl),y
		tax
		iny
		lda	(itempl),y
		sta	itemph
		stx	itempl
		bra	pscan1

SysJMP		JSR	Main_Loop	; in DiskOS
		rts

ToHex		PHA                     ;  prints AA hex digits
		LSR                     ;  MOVE UPPER NIBBLE TO LOWER
		LSR                     ;
		LSR                     ;
		LSR                     ;
		JSR   SaveDig           ;
		PLA                     ;
SaveDig		AND   #$0F              ;
		CMP   #$0A              ;
		BCC   SaveDig1          ;
		ADC   #$66              ;
SaveDig1	EOR   #$30              ;
		sta   buffer+2,x        ;
		inx
		rts

And another approach:

There seems to be quite a lot of mixed information about how to deal with string arguments in LOAD and SAVE. I’d like to present the way I came up with:
At the beginning of either LOAD and SAVE, i call a subroutine that gets the string argument using LAB_EVEX and opens the file:

openfile:
                jsr LAB_EVEX
                lda Dtypef
                bne @go
                ; not a string, trigger syntax error
                ldx #$02
                jsr LAB_XERR
@go:
                ldy #$00
@l:
                lda (ssptr_l),y
                beq @open
                cmp #'"'
                beq @term
                iny
                bne @l
@term:
                lda #$00
                sta (ssptr_l),y
@open:
                lda ssptr_l
                ldx ssptr_h
                jsr krn_open
                bne     io_error
                rts
io_error:
                pha
                jsr     krn_primm
                .asciiz "io error: "
                pla
                jmp krn_hexout

As it seems, after calling LAB_EVEX, ssptr points to the string given as parameter including the closing “, which I overwrite with $00 to be compatible with my open routine, which expects the address of a null terminated string.
In another thread, it was suggested to pop the string from the descriptor stack using LAB_22B6, which in my case resulted in ut1_ph/l not pointing to the string, but anywhere in the area of $exxx, where my os resides. So I went for the above approach. It works, but it feels rather dirty due to the overwriting of the closing “. At this point, I am open for suggestions.

post

IDE bus interface circuit

IDE bus interface circuit

Hardware

IDE Bus interface circuit.

The connector on the top left of the diagram is from my own 6502 boards (see the sbc project) and is as it is for two reasons. It’s easy
to wire on a stripboard layout and I have a lot of 26 way ribbon, headers and plugs. All the signals are directly from the 6502 except /SEL0 and /SEL1 which are used to select the block $F1xx with /SEL0 = 1 and /SEL1 = 0.

A 40 pin IDC header, re-cycled from a PC motherboard, is used to connect to the IDE bus.

All the capacitors are low ESR electrolytics and are placed one near each chip. If you don’t have this type to hand you can use standard electrolytics with some low value
ceramic capacitor, say 0.1uF, in parallel.

The GAL16V8A is used to generate the read and write strobes for the buffers and latches and to generate the IOR and IOW strobes for the IDE bus. All are active low. The interface uses 32 bytes in the address range as it is a 16 bit port with 16 addresses. For anyone interested the equations for this chip are in ide_01.pld and can be compiled with WinCUPL. The fuse file, ide_01.jed and the compiler listing, ide_01.txt are also included.

IDE ATA Interface address map. High byte at address+1
Address Read Write Width
$00 Data 16 Bit
$02 Error Precomp 8 Bit
$04 Sector count 8 Bit
$06 LBA bits 0 to 8 / CHS Sector number 8 Bit
$08 LBA bits 8 to 15 / CHS Cylinder low 8 Bit
$0A LBA bits 16 to 23 / CHS Cylinder high 8 Bit
$0C Drive select & LBA bits 24 to 27 / CHS Head select 8 Bit
$0E Status Command 8 Bit
$1C Alternate status Device control 8 Bit
$1E Drive address Not used 8 Bit

For ATAPI devices some of the register names and functions change. Here is the ATAPI map.

IDE ATAPI Interface address map. High byte at
address+1
Address Read Write Width
$00 Data 16 Bit
$02 Error Feature 8 Bit
$04 Interrupt reason Not used 8 Bit
$06 Not used
$08 Byte count low 8 Bit
$0A Byte count high 8 Bit
$0C Device select 8 Bit
$0E Status Command 8 Bit
$1C Alternate status Device control 8 Bit
$1E Not used

All reads and writes to the IDE bus are sixteen bits wide. On write, first the lower eight bits are written to the even address (A0 = 0) and latched by one 74LS574, Then the upper eight bits are written to the corresponding odd address (A0 = 1) and are presented to the upper eight bits of the IDE bus by the 74LS245, a bi-directional buffer used here in the one direction only. These eight bits, along with the previously latched lower
eight bits, make up the sixteen bits needed for a write operation.
On read the lower eight bits are read from the IDE bus even address (A0 = 0) via the second 74LS245 and, as this read takes place the upper, eight bits from the IDE bus are latched by the second 74LS574. The upper eight bits can then be read from the 74LS574 latch.

The interface is arranged thus so that ..

  • All 16 bits are used, this allows us to utilise the full capacity of the drive.
  • Reads and writes are symmetrical, i.e. both are done low byte and then high byte.
  • The status registers on ATA devices are all on the low byte, fast polling is possible because only the low byte needs to be read.

The disadvantage is that every write must be sixteen bits, even if the top eight bits
aren’t used.

Other features are ..

  • An activity LED. Illuminates during ATA device activity. If required this could be wired off board.
  • A reset jumper. This ties the IDE bus reset to the CPU reset so both are reset together.
  • An IRQ jumper. This is to allow an interrupt based driver to be developed.

The finished board.

Close-up views.

Software.
At present only a BASIC demonstration program is available. It can be seen here.
Download this file

10 REM set disk interface values
20 REM enhanced BASIC version
30 REM supports ATA but not ATAPI devices
100 AD=$F100 : REM interface base address
110 IO=AD+$00 : REM input/output register - 16 bits
120 ER=AD+$02 : REM error/precomp register
130 SC=AD+$04 : REM sector count
140 SS=AD+$06 : REM LBA bits 0 to 7
150 CL=AD+$08 : REM LBA bits 8 to 15
160 CH=AD+$0A : REM LBA bits 26 to 23
170 HR=AD+$0C : REM drive/LBA bits 24 to 27
180 SR=AD+$0E : REM status/command
190 RR=AD+$0C+$10 : REM alternate status/device control
200 AS=AD+$0E+$10 : REM drive address
210 MD=%10100000 : REM these bits must always be set
220 BK=%01000000 : REM value to set LBA mode
1000 REM set parameters to defaults
1005 DOKE RR,6 : REM write device control
1010 DOKE SC,1 : REM sector count
1020 DOKE SS,0 : REM LBA bits 0 to 7
1030 DOKE CL,0 : REM LBA bits 8 to 15
1040 DOKE CH,0 : REM LBA bits 26 to 23
1050 DOKE HR,MD+BK : REM drive/LBA upper 4 bits
1055 DOKE RR,2 : REM write device control
1060 GOSUB 1300
1065 PRINT : GOSUB 3100
1070 PRINT
1080 PRINT "0  Select drive"
1090 PRINT "1  Reset device"
1100 PRINT "2  Identify device"
1110 PRINT "3  Sleep device"
1120 PRINT "4  Idle device"
1130 PRINT "5  Standby device"
1140 PRINT "6  Query power mode"
1150 PRINT "7  Execute diagnostics"
1160 PRINT "8  Recalibrate"
1170 PRINT "9  Test read"
1175 PRINT "10 Write BASIC""
1180 PRINT
1190 PRINT "Choose one";
1200 INPUT I
1210 I=I+1
1220 IF (I<1) OR (I>11) THEN 1200
1230 PRINT
1240 ON I GOSUB 3000,1300,6000,2200,2300,2400,2500,2600,2900,2700,20000
1250 GOTO 1065
1300 REM reset device(s)
1310 DOKE HR,(MD+BK) EOR 16 : REM select other drive
1320 CMD=8
1330 GOSUB 11020
1340 DOKE HR,MD+BK : REM select drive
1350 CMD=8
1360 GOSUB 11020
1370 RETURN
2200 REM go to sleep
2210 CMD=230
2220 DOKE SR,CMD : REM send command
2230 RETURN
2300 REM go to idle mode
2310 CMD=225
2320 GOTO 11000
2400 REM go to standby mode
2410 CMD=224
2420 GOTO 11000
2500 REM query device power mode
2510 CMD=229
2520 GOSUB 11000
2530 I=DEEK(SC) AND 255 : REM sector count
2540 PRINT "Device is in ";
2550 IF I=0 THEN PRINT "standby"; : GOTO 2590
2560 IF I=128 THEN PRINT "idle"; : GOTO 2590
2570 IF I=255 THEN PRINT "active"; : GOTO 2590
2580 PRINT "unknown";
2590 PRINT " mode"
2595 RETURN
2600 REM execute diagnostics
2610 CMD=144
2620 GOSUB 11000
2630 I=(DEEK(ER) AND 255) : REM read error register
2640 PRINT "Device 0 ";
2650 IF (I AND 127)=1 THEN PRINT "passed" : GOTO 2670
2660 PRINT "failed"
2670 PRINT "Device 1 ";
2680 IF (I AND 128) THEN PRINT "failed" : GOTO 2695
2690 PRINT "passed or not present"
2695 RETURN
2700 REM test read
2710 INPUT "Sector number";IX
2725 GOSUB 10000
2730 CMD=32
2740 GOSUB 11000
2750 REM display sector
2760 S$=""
2770 PRINT
2780 FOR WC=0 TO 255
2790 GOSUB 12000
2800 HH=(HI/256) AND 127
2810 IF HH < 32 THEN HH=46
2815 HL=HI AND 127
2820 IF HL <32 THEN HL=46
2825 S$=S$+CHR$(HL)
2830 S$=S$+CHR$(HH)
2865 PRINT " ";HEX$(HI,4);
2870 IF (WC AND 7)< >7 THEN 2885
2875 PRINT "    ";S$
2880 S$=""
2885 NEXT
2890 RETURN
2900 REM recalibrate device (seek to 0)
2910 IX=0
2920 GOSUB 10000
2930 CMD=16
2940 GOTO 11000
3000 REM change drive selection
3010 DO : INPUT "Which drive [0/1]";I
3020 LOOP WHILE (I AND -2)
3030 MD=MD AND 239
3040 IF I THEN MD=MD OR 16
3050 DOKE HR,MD+BK : REM select drive
3060 RETURN
3100 REM display allegedly selected drive
3110 PRINT "Drive ";
3120 IF (MD AND 16) THEN PRINT "1"; :GOTO 3140
3130 PRINT "0";
3140 PRINT " selected"
3150 RETURN
6000 REM read ID data and display drive parameters
6010 CMD=236
6020 GOSUB 11000
6030 GOSUB 12000
6035 REM will identify some ATAPI devices
6040 PRINT "ATA";
6050 IF (HI AND -32768) THEN PRINT "PI";
6060 PRINT " Device - ";
6070 IF (HI AND 128)=0 THEN PRINT "non";
6080 PRINT "removable media"
6085 PRINT
6090 GOSUB 12000 : CYL=HI
6100 GOSUB 12000
6110 GOSUB 12000 : HE=HI
6120 GOSUB 12000 : GOSUB 12000
6130 GOSUB 12000 : SPT=HI
6140 GOSUB 12000 : GOSUB 12000 : GOSUB 12000
6150 PRINT "Cylinders",CYL
6160 PRINT "Heads",HE
6170 PRINT "Sectors",SPT
6180 SIZE = CYL*HE*SPT/2048
6190 PRINT "Capacity",SIZE;" MB"
6195 PRINT
6200 PRINT "Serial # "," "; : CNT=10 : GOSUB 7000 : PRINT
6210 GOSUB 12000 : GOSUB 12000 : GOSUB 12000
6220 PRINT "Rev: "," "; : CNT=4 : GOSUB 7000 : PRINT
6230 PRINT "Model: "," "; : CNT=20 : GOSUB 7000 : PRINT
6240 PRINT
6250 FOR I=1 TO 7
6260 GOSUB 12000
6270 NEXT
6280 GOSUB 12000 : PRINT "Current logical cylinders",HI
6300 GOSUB 12000 : PRINT "Current logical heads",HI
6310 GOSUB 12000 : PRINT "Current logical sectors",HI
6320 GOSUB 12000 : CAP=HI
6330 GOSUB 12000 : CAP=HI*65536+CAP
6340 PRINT "Current capacity in sectors",CAP
6350 GOSUB 12000 : GOSUB 12000 : CAP=HI
6360 GOSUB 12000 : CAP=HI*65536+CAP
6370 PRINT "User addressable LBA sectors";CAP
6380 FOR I=62 TO 255 : GOSUB 12000 : NEXT
6400 RETURN
7000 REM read CNT words and output ASCII
7010 FOR I=1 TO CNT
7020 LO=PEEK(IO) : REM read data
7030 HI=PEEK(IO+1) : REM read data
7040 IF HI > 31 THEN PRINT CHR$(HI);
7050 IF LO > 31 THEN PRINT CHR$(LO);
7060 NEXT
7070 RETURN
10000 REM Set LBA block number and sector count of 1
10005 REM As BASIC only has 24 significant bits this
10006 REM routine will break if the block number is
10007 REM greater than 16777216 (just over 8GB limit!)
10010 I1=IX AND 255
10020 IX=IX/256 : I2=IX AND 255
10030 IX=IX/256 : I3=IX AND 255
10040 IX=IX/256 : I4=IX AND 15
10050 DOKE HR,I4+MD+BK : REM drive/LBA upper 4 bits
10060 DOKE SS,I1 : REM LBA bits 0 to 7
10070 DOKE CL,I2 : REM LBA bits 8 to 15
10080 DOKE CH,I3 : REM LBA bits 26 to 23
10090 DOKE SC,1 : REM sector count
10100 RETURN
11000 REM send command to drive, ensure ready
11010 DO : LOOP UNTIL (PEEK(SR) AND 192)=64 : REM get status
11020 DOKE SR,CMD : REM send command
11030 DO : I=PEEK(SR) : REM get status
11040 LOOP UNTIL (I AND 128)=0
11050 IF (I AND 1)=0 THEN RETURN
11060 I=PEEK(ER) : PRINT "IDE Error, command";CMD : REM read error register
11062 IF (I AND 2) THEN PRINT "No media"
11064 IF (I AND 4) THEN PRINT "Aborted"
11066 IF (I AND 8) THEN PRINT "Media change requested"
11068 IF (I AND 16) THEN PRINT "Sector ID not found"
11070 IF (I AND 32) THEN PRINT "Media changed"
11072 IF (I AND 64) THEN PRINT "Uncorrectable error"
11090 RETURN
12000 REM read one word, return it in HI
12010 WAIT SR,16 : REM wait status
12020 HI=DEEK(IO) : REM read data
12030 RETURN
20000 REM write C000h-DFFDh to disk
20010 AA=$C000
20030 FOR WX = 1 TO 16
20040 PRINT "Writing sector";WX
20050 IX=WX
20060 GOSUB 10000
20070 CMD=48
20080 GOSUB 11000
20090 FOR WC=1 TO 256
20120 HI=DEEK(AA)
20140 AA=AA+2
20150 GOSUB 22000
20160 NEXT
20190 NEXT
20999 RETURN
22000 REM write one word in HI
22010 DO : LOOP UNTIL (PEEK(SR) AND 144)=16 : REM get status
22020 DOKE IO,HI : REM write data
22030 RETURN

LazyPROM

LazyPROM – The idle person’s EPROM emulator.

Even with a new EPROM eraser that can handle 14 EPROMs at once I got fed up going through the program/test/erase cycle that is part of developing code.
I did think, briefly, of building a full featured EPROM emulator, but that was far too much like work. There had to be an easier way.
As I have a few old Dallas smart sockets kicking about I decided that one of them, with a 32k low power CMOS RAM, would be ideal.

The circuit.

The circuit is very simple. The pinout for a standard 32k ROM and standard 32k EPROM differ on only two pins. This means most of the circuit is just pin for pin connections as can be seen in the diagram below.


LazyPROM diagram.
With the switch in the ‘RAM’ position all the pins are connected pin to pin and the RAM can be programmed either in a programmer or as part of a microprocessors main memory. With the switch in the ‘EPROM’ position the RAM’s R/W line is disconnected from the header and pin 27 on the header is now connected to the RAM’s A14 pin. This makes the RAM chip look like a 32k EPROM to the target socket.

Construction.

The whole thing was put together on a small piece of prototyping board in well under an hour. The most dificult part was soldering the headers to the track side of the board.
Note – Don’t be tempted to use anything but hard, gold, turned pin header pins for the header. The, easier to find, square pins will either not fit, or damage, most IC sockets and the cheaper tinned header pins will be damaged more easily during use.


 

In use.

As no special voltages or timings are needed to program the RAM in the smart socket it would be fairly easy to make a programmer for this, if you don’t already have something suitable. The Dallas smart socket is supposed to retain RAM contents for up to 10 years but the
ones I have, being very second user, are an unknown quantity. They certainly work for a week or so though one was originally completely dead and required some surgery to replace both batteries. More on that elsewhere.
The first one I built proved to be so usefull that I’ve since built another and both have proved invaluable in hacking the Ferguson SRB1 DMAC receiver.