Commits

Ross Light  committed 6e44f3d

Add output stream model

  • Participants
  • Parent commits f690618

Comments (0)

Files changed (2)

File north/exec.go

 		if err != nil {
 			return err
 		}
-		return m.ui.Output(m.window, s)
+		return m.out(s)
 	case 0x8:
 		// call_1s
 		if ops[0] == 0 {
 		if err != nil {
 			return err
 		}
-		return m.ui.Output(m.window, s)
+		return m.out(s)
 	case 0xb:
 		// ret
 		return m.routineReturn(ops[0])
 		if err != nil {
 			return err
 		}
-		return m.ui.Output(m.window, s)
+		return m.out(s)
 	case 0xe:
 		// load
 		m.setVariable(in.storeVariable, m.getVariable(uint8(ops[0])))
 		return m.routineReturn(0)
 	case 0x2:
 		// print
-		return m.ui.Output(m.window, in.text)
+		return m.out(in.text)
 	case 0x3:
 		// print_ret
-		if err := m.ui.Output(m.window, in.text + "\n"); err != nil {
+		if err := m.out(in.text + "\n"); err != nil {
 			return err
 		}
 		return m.routineReturn(1)
 		return ErrQuit
 	case 0xb:
 		// new_line
-		return m.ui.Output(m.window, "\n")
+		return m.out("\n")
 	case 0xc:
 		// show_status
 		if m.Version() <= 3 {
 		if err != nil {
 			return err
 		}
-		return m.ui.Output(m.window, string(r))
+		return m.out(string(r))
 	case 0x6:
 		// print_num
-		return m.ui.Output(m.window, fmt.Sprint(int16(ops[0])))
+		return m.out(fmt.Sprint(int16(ops[0])))
 	case 0x7:
 		// random
 		if ops[0] == 0 {
 		// TODO
 	case 0x13:
 		// output_stream
-		// TODO
+		switch int16(ops[0]) {
+		case 0:
+			// do nothing
+		case screenOutput:
+			m.streams |= 1 << screenOutput
+		case -screenOutput:
+			m.streams &^= 1 << screenOutput
+		case transcriptOutput:
+			m.streams |= 1 << transcriptOutput
+		case -transcriptOutput:
+			m.streams &^= 1 << transcriptOutput
+		case redirectOutput:
+			m.streams |= 1 << redirectOutput
+			if len(m.rtables) == cap(m.rtables) {
+				return instructionError{Instruction: in, Err: errors.New("Too many output redirection levels")}
+			}
+			addr := Address(ops[1])
+			m.rtables = append(m.rtables, rtable{addr, addr + 2})
+			m.storeWord(addr, 0)
+		case -redirectOutput:
+			if len(m.rtables) > 1 {
+				m.rtables = m.rtables[:len(m.rtables)-1]
+			} else {
+				m.rtables = m.rtables[:0]
+				m.streams &^= 1 << redirectOutput
+			}
+		default:
+			return instructionError{Instruction: in, Err: fmt.Errorf("Invalid output stream: %d", int16(ops[0]))}
+		}
 	case 0x15:
 		// sound_effect
 		if player, ok := m.ui.(SoundPlayer); ok {
 		// TODO
 	case 0x0b:
 		// print_unicode
-		m.ui.Output(m.window, string(rune(ops[0])))
+		return m.out(string(rune(ops[0])))
 	default:
 		return instructionError{Instruction: in, Err: errors.New("EXT opcode not implemented yet")}
 	}

File north/machine.go

 	FinishSound(n int) error
 }
 
+// Output streams
+const (
+	screenOutput = 1 + iota
+	transcriptOutput
+	redirectOutput
+	readOutput
+
+	numOutputStreams
+)
+
+// rtable is a redirect table pointer.
+type rtable struct {
+	Start Address
+	Curr  Address
+}
+
 type Machine struct {
 	memory []byte
 	stack  []stackFrame
 	ui     UI
-	window int
 	rand   *rand.Rand
+
+	window  int
+	streams uint8
+	rtables []rtable
 }
 
 // NewMachine creates a new machine, loaded with the story from r.
 	}
 	m.memory = newMemory
 	m.stack = make([]stackFrame, 1)
+	m.rtables = make([]rtable, 0, 16)
+	m.streams = 1<<screenOutput | 1<<transcriptOutput
 	m.seed()
 
 	// TODO: In version 6+, this is a routine, not a direct PC.
 	}
 }
 
+// out handles output. This is sent to the UI, unless redirection has been
+// turned on.
+func (m *Machine) out(s string) error {
+	if m.streams&(1<<redirectOutput) != 0 {
+		// If redirect is selected, no other streams get output.
+		tab := &m.rtables[len(m.rtables)-1]
+		m.storeWord(tab.Start, m.loadWord(tab.Start)+Word(len(s)))
+		for _, r := range s {
+			// rune should already be ZSCII-clean, since we wrote it.
+			m.memory[tab.Curr] = byte(r)
+			tab.Curr++
+		}
+		return nil
+	}
+	if m.streams&(1<<screenOutput) != 0 {
+		if err := m.ui.Output(m.window, s); err != nil {
+			return err
+		}
+	}
+	// TODO: transcript, etc.
+	return nil
+}
+
 func (m *Machine) refreshStatusLine() error {
 	liner, ok := m.ui.(StatusLiner)
 	if !ok {