Skip to content

Commit

Permalink
Support several ROMs. *BASIC
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanizag committed Aug 17, 2021
1 parent 8e39b1f commit 537c63a
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 38 deletions.
67 changes: 65 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ References:
## Features
- Some of the MOS entrypoints and VDU control codes are defined.
- Can run BBC BASIC and most of the language ROMs.
- Can load up to 16 sideways ROMs.
- Saves and loads files from the host filesystem.
- Does some of the mode 7 text coloring using ANSI escape codes on the terminal. Try `VDU 65,129,66,130,67,132,68,135,69,13,10` on BBC BASIC.
- OSCLI comands suported:
Expand All @@ -39,8 +40,8 @@ References:
bbz [flags] [filename]
```

`filename` is the filename of the ROM to run. With no arguments it
runs the BBC Basic ROM in `BASIC.ROM`.
`filename` is the filename of the ROM to run (the same as `-rom0`). By default, it
uses the BBC Basic ROM in `BASIC.ROM`.

AvaIlable flags (to put before the ROM filename if present):

Expand All @@ -50,6 +51,40 @@ AvaIlable flags (to put before the ROM filename if present):
-m dump to the console the MOS calls excluding console I/O calls
-p panic on not implemented MOS calls
-s dump to the console the accesses to Fred, Jim or Sheila
-rom0 string
filename for rom 0 (slot 0xf)
-rom1 string
filename for rom 1 (slot 0xe)
-rom2 string
filename for rom 2 (slot 0xd)
-rom3 string
filename for rom 3 (slot 0xc)
-rom4 string
filename for rom 4 (slot 0xb)
-rom5 string
filename for rom 5 (slot 0xa)
-rom6 string
filename for rom 6 (slot 0x9)
-rom7 string
filename for rom 7 (slot 0x8)
-rom8 string
filename for rom 8 (slot 0x7)
-rom9 string
filename for rom 9 (slot 0x6)
-rom10 string
filename for rom 10 (slot 0x5)
-rom11 string
filename for rom 11 (slot 0x4)
-rom12 string
filename for rom 12 (slot 0x3)
-rom13 string
filename for rom 13 (slot 0x2)
-rom14 string
filename for rom 14 (slot 0x1)
-rom15 string
filename for rom 15 (slot 0x0)
```

Expand Down Expand Up @@ -108,3 +143,31 @@ Using mode 7 colors:

![mode 7 colors](doc/vdu_colors.png)

Using several ROMs at once:
```
$ go run *.go -rom0 BASIC.ROM -rom1 ROMs/Forth_103.rom -rom2 ROMs/LISP-200.rom -rom3 ROMs/COMAL.rom
bbz - Acorn MOS for 6502 adaptation layer, https://github.com/ivanizag/bbz
(tip: uppercase is usually needed)
BASIC - (C)1982 Acorn
>*LISP
LISP - (C)1982 Acornsoft/1979 Owl Computers
Evaluate : (* '*FORTH)
FORTH - (C) Acornsoft Ltd. 1983
COLD or WARM start (C/W)? C
FORTH
OK
OS' *COMAL'
COMAL - (C)Acorn
→*BASIC
BASIC - (C)1982 Acorn
>
```
4 changes: 2 additions & 2 deletions asm/firmware.s
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


; constants
.exportzp ROM_SELECT := $f4
.exportzp ROM_SELECT := $f4
.export ROM_TABLE := $023a
.export LANGUAGE_ENTRY := $8000
.export SERVICE_ENTRY := $8003
Expand Down Expand Up @@ -82,7 +82,7 @@ OSBYTE_143: lda ROM_SELECT ; Get current ROM number
ldx #$0f ; Start at ROM 15
; Issue service call loop
NEXT: inc ROM_TABLE,X ; Read bit 7 on ROM type table (no ROM has type 254 &FE)
dec ROM_TABLE,X ;
dec ROM_TABLE,X ;
bpl SKIP ; If not set (+ve result), step to next ROM down
stx ROM_SELECT ; Otherwise, select this ROM, &F4 RAM copy
stx ROM_LATCH ; Page in selected ROM
Expand Down
14 changes: 0 additions & 14 deletions bbz.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,6 @@ import (
*/

func RunMOS(env *environment) {

/*
Next, the MOS will set the error point at &FD/&FE to point at the version string (or copyright
message if no version string is present).
*/
copyrightAddress := 0x8000 + 1 + uint16(env.mem.Peek(romCopyrightOffsetPointer))
copyrigt := env.mem.getString(copyrightAddress, 0)
env.mem.pokeWord(zpErrorPointer, copyrightAddress)
/*
The MOS also automatically prints the ROM's title string (&8009) so that the user is acknowledged.
*/
language := env.mem.getString(romTitleString, 0)
fmt.Printf("%s - %s\n", language, copyrigt)

// Execute
for !env.stop {
env.cpu.ExecuteInstruction()
Expand Down
34 changes: 34 additions & 0 deletions environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,40 @@ func (env *environment) getFile(handle uint8) *os.File {
return nil
}

func (env *environment) initUpperLanguage() {
for slot := 0xf; slot >= 0; slot-- {
romType := env.mem.data[romTypeTable+uint16(slot)]
if romType&0x40 != 0 {
env.initLanguage(uint8(slot))
return
}
}

panic("There is no language ROM available to boot")
}

func (env *environment) initLanguage(slot uint8) {
env.mem.Poke(zpROMSelect, slot)
env.mem.Poke(sheila_rom_latch, slot)

/*
Next, the MOS will set the error point at &FD/&FE to point at the version string (or copyright
message if no version string is present).
*/
copyrightAddress := 0x8000 + 1 + uint16(env.mem.Peek(romCopyrightOffsetPointer))
copyrigt := env.mem.getString(copyrightAddress, 0)
env.mem.pokeWord(zpErrorPointer, copyrightAddress)
/*
The MOS also automatically prints the ROM's title string (&8009) so that the user is acknowledged.
*/
language := env.mem.getString(romTitleString, 0)
fmt.Printf("%s - %s\n", language, copyrigt)

_, x, y, p := env.cpu.GetAXYP()
env.cpu.SetAXYP(1, x, y, p)
env.cpu.SetPC(romStartAddress)
}

func (env *environment) raiseError(code uint8, msg string) {
/*
The BBC microcomputer adopts a standard pattern of bytes
Expand Down
43 changes: 25 additions & 18 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,26 @@ func main() {
"p",
false,
"panic on not implemented MOS calls")
rom0 := flag.String(
"rom",
"",
"filename for rom 0 (slot 0xf)")
rom1 := flag.String(
"rom1",
"",
"filename for rom 1 (slot 0xe)")
flag.Parse()

romFile := flag.Arg(0)
if *rom0 != "" {
romFile = *rom0
roms := make([]*string, 16)
for i := 0; i < 16; i++ {
roms[i] = flag.String(
fmt.Sprintf("rom%v", i),
"",
fmt.Sprintf("filename for rom %v (slot 0x%x)", i, 15-i))
}
if romFile == "" {
romFile = "BASIC.ROM"

flag.Parse()

if *roms[0] == "" {
romFile := flag.Arg(0)
if romFile == "" {
def := "BASIC.ROM"
roms[0] = &def
} else {
roms[0] = &romFile
}

}

env := newEnvironment(*traceCPU,
Expand All @@ -54,11 +58,14 @@ func main() {
*panicOnErr)

env.mem.loadFirmware("firmware")
env.mem.loadRom(romFile, 0xf)
env.mem.Poke(zpROMSelect, 0xf)
if *rom1 != "" {
env.mem.loadRom(*rom1, 0xe)

for i, rom := range roms {
if *rom != "" {
env.mem.loadRom(*rom, uint8(0xf-i))
}
}

env.initUpperLanguage()

RunMOS(env)
}
24 changes: 24 additions & 0 deletions osByte.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,19 @@ func execOSBYTE(env *environment) {
*/
// TODO

case 0x8e:
option = "Enter language ROM"
/*
Entry parameters: X determines which language ROM is entered
The selected language will be re-entered after a soft BREAK.
The action of this call is to printout the language name and enter the selected
language ROM at &8000 with A=1. Locations &FD and &FE in zero page point to the
copyright message in the ROM. When a Tube is present this call will copy the
language across to the second processor.
*/
env.initLanguage(x)
newA = 1

case 0xca:
option = "Read/write keyboard status byte"
/*
Expand Down Expand Up @@ -182,6 +195,17 @@ func execOSBYTE(env *environment) {
env.notImplemented("OSBYTEda for x or y not zero")
}

case 0xea:
option = "R/W Tube present flag"
/*
The value 255 indicates the Tube is present 0 indicates it is not
Writing to this value is not recommended
We return 0 always
*/
newX = 0
newY = 0

default:
env.notImplemented(fmt.Sprintf("OSBYTE%02x", a))
/*
Expand Down
23 changes: 21 additions & 2 deletions osCLI.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func execOSCLI(env *environment) {
// The command-line interpreter does not distinguish between upper and lower case characters in the command name
command = strings.ToUpper(command)
params := fields[1:]
handled := false

if command == "*" && len(params) > 0 {
// There are spaces between the * and the command
Expand Down Expand Up @@ -61,11 +62,13 @@ func execOSCLI(env *environment) {
for example.
*/
// do nothing
handled = true

case "*H.":
fallthrough
case "*HELP":
msg = "bbz - Acorn MOS for 6502 adaptation layer, https://github.com/ivanizag/bbz"
handled = true

// TODO: multiple ROMS: service call 9 after the MOS message

Expand All @@ -74,9 +77,11 @@ func execOSCLI(env *environment) {
case "*CAT":
// TODO
msg = "<<directory placeholder>>"
handled = true

case "*QUIT":
env.stop = true
handled = true

case "*HOST":
if len(params) == 0 {
Expand All @@ -89,6 +94,7 @@ func execOSCLI(env *environment) {
}
fmt.Println(string(stdout))
}
handled = true

case "*FX":
// Parse *FX args
Expand Down Expand Up @@ -117,14 +123,27 @@ func execOSCLI(env *environment) {
// Send to OSBYTE
env.cpu.SetAXYP(uint8(argA), uint8(argX), uint8(argY), p)
execOSBYTE(env)
handled = true

case "*BASIC":
// Runs the first language ROM with no service entry
for slot := 0xf; slot >= 0; slot-- {
romType := env.mem.data[romTypeTable+uint16(slot)]
if romType&0b1100_0000 == 0b0100_0000 { // bit 7 clear, bit 6 set
env.initLanguage(uint8(slot))
handled = true
break
}
}
}

default:
if !handled {
// Send to the other ROMS if available.
env.mem.pokeWord(zpStr, xy)
cmd := uint8(4) // Unrecognized command
env.cpu.SetAXYP(cmd, x, 1, p)
env.cpu.SetPC(procCLITOROMS)
// procCLITOROMS issues a 254-Bad command if the command is not handles by any ROM
// procCLITOROMS issues a 254-Bad command if the command is not handled by any ROM
}

if msg != "" {
Expand Down

0 comments on commit 537c63a

Please sign in to comment.