Skip to content

Commit

Permalink
WIP add Trap GETC 0x23
Browse files Browse the repository at this point in the history
Echoes a prompt, waits for a character to be typed, echoes the character, and
returns the value in R0.
  • Loading branch information
smoynes committed Dec 12, 2023
1 parent 010e2d4 commit f612fe3
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 0 deletions.
105 changes: 105 additions & 0 deletions internal/monitor/traps.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,108 @@ var TrapPuts = Routine{
/*0x0470 */ &asm.FILL{LITERAL: uint16(vm.DDRAddr)}, // data-registers.
},
}

// TrapGetc is the system call to prompt the user and wait for a character of
// input.
//
// - Table: 0x0000
// - Vector: 0x23
// - Handler: 0x04a0
//
// Adapted from Fig. 9.12, 3/e.
var TrapGetc = Routine{
Name: "GETC",
Vector: vm.TrapTable + vm.Word(vm.TrapHALT),
Orig: 0x04a0,
Symbols: asm.SymbolTable{
"START": 0x04a0,
"L1": 0x04a4,
"LOOP": 0x04a8,
"L2": 0x04aa,
"INPUT": 0x04af,
"L3": 0x04b2,
"L4": 0x04b5,

"SAVER1": 0x04bc,
"SAVER2": 0x04bd,
"SAVER3": 0x04be,
"NEWLINE": 0x04bf,
"DSR": 0x04c0,
"DDR": 0x04c1,
"KBSR": 0x04c2,
"KBDR": 0x04c3,
"PROMPT": 0x04c4,
},
Code: []asm.Operation{
// Store registers to restore before return.
&asm.ST{SR: "R1", SYMBOL: "SAVER1"},
&asm.ST{SR: "R2", SYMBOL: "SAVER2"},
&asm.ST{SR: "R3", SYMBOL: "SAVER3"},
&asm.LD{DR: "R2", SYMBOL: "NEWLINE"},

/*L1:0x04a4*/
&asm.LDI{DR: "R1", SYMBOL: "DSR"}, // Check if DDR is empty.
&asm.BR{NZP: asm.CondZP, SYMBOL: "L1"},
&asm.STI{SR: "R2", SYMBOL: "DDR"}, // Echo newline.

// Load prompt address
&asm.LEA{DR: "R1", SYMBOL: "PROMPT"},

/*LOOP:0x04a8*/
&asm.LDR{DR: "R0", SR: "R1", OFFSET: 0},
&asm.BR{NZP: asm.CondZP, SYMBOL: "INPUT"},
/*L2:0x04aa*/
&asm.LDI{DR: "R3", SYMBOL: "DSR"}, // Check again if DDR is ready.
&asm.BR{NZP: asm.CondZP, SYMBOL: "L2"},
&asm.STI{SR: "R0", SYMBOL: "DDR"}, // Echo next prompt char.

&asm.ADD{DR: "R1", SR1: "R1", LITERAL: 1}, // Increment prompt pointer.
&asm.BR{NZP: asm.CondNZP, SYMBOL: "LOOP"}, // Iterate to LOOP.

/*INPUT:0x04af*/
&asm.LDI{DR: "R3", SYMBOL: "KBSR"}, // Check for KBD ready.
&asm.BR{NZP: asm.CondZP, SYMBOL: "INPUT"},
&asm.LDI{DR: "R0", SYMBOL: "KBDR"}, // Load KBD input into trap param.

/*L3:0x04b2*/
&asm.LDI{DR: "R3", SYMBOL: "DSR"}, // Check yet again if DDR ready.
&asm.BR{NZP: asm.CondZP, SYMBOL: "L3"},
&asm.STI{SR: "R0", SYMBOL: "DDR"}, // Echo input.

/*L4:0x04b5*/
&asm.LDI{DR: "R3", SYMBOL: "DSR"}, // Check once moce if DDR is ready.
&asm.BR{NZP: asm.CondZP, SYMBOL: "L4"},
&asm.STI{SR: "R2", SYMBOL: "DDR"}, // Echo newline.

// Restore work registers.
&asm.LD{DR: "R1", SYMBOL: "SAVER1"},
&asm.LD{DR: "R2", SYMBOL: "SAVER2"},
&asm.LD{DR: "R3", SYMBOL: "SAVER3"},

// Return from trap.
&asm.RTI{},

// Stored register allocations.
/*SAVER1:0x04bc*/
&asm.BLKW{ALLOC: 0x0001},
/*SAVER2:0x04bd*/
&asm.BLKW{ALLOC: 0x0001},
/*SAVER3:0x04be*/
&asm.BLKW{ALLOC: 0x0001},

// Address constants.
/*DSR:0x04bf*/
&asm.FILL{LITERAL: 0xfe02},
/*DDR:0x04c0*/
&asm.FILL{LITERAL: 0xfe04},
/*KBSR:0x04c1*/
&asm.FILL{LITERAL: 0xfe00},
/*DDR:0x04c2*/
&asm.FILL{LITERAL: 0xfe02},
/*NEWLINE:0x04c3*/
&asm.FILL{LITERAL: 0x000a},

/*PROMPT:0x04c4*/
&asm.STRINGZ{LITERAL: "Input a character>"},
},
}
54 changes: 54 additions & 0 deletions internal/monitor/traps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,60 @@ func (*trapHarness) Logger() *log.Logger {
return log.DefaultLogger()
}

func TestTrap_Getc(tt *testing.T) {
t := NewHarness(tt)

obj, err := Generate(TrapGetc)

if err != nil {
t.Error(err)
}

if len(obj.Code) < 10 {
t.Error("code way too short", len(obj.Code))
return
}

image := SystemImage{
logger: t.Logger(),
Symbols: nil,
Traps: []Routine{TrapGetc},
}

machine := vm.New(
WithSystemImage(&image),
)

// Now, we create code to execute the trap under test.
code := vm.ObjectCode{
Orig: 0x3000,
Code: []vm.Word{
vm.NewInstruction(vm.TRAP, 0x23).Encode(),
},
}

loader := vm.NewLoader(machine)

unsafeLoad(loader, code)

for i := 0; i < 500; i++ {
err = machine.Step()

if testing.Verbose() {
t.Logf("Stepped\n%s\n%s\nerr %v", machine, machine.REG, err)
}

if errors.Is(err, vm.ErrHalted) {
break
} else if !machine.MCR.Running() {
break
} else if err != nil {
t.Error(err)
break
}
}
}

func TestTrap_Halt(tt *testing.T) {
t := NewHarness(tt)

Expand Down
1 change: 1 addition & 0 deletions internal/vm/intr.go
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ const (
TrapTable = Word(0x0000) // TRAP (0x0000:0x00ff)
TrapOUT = uint8(0x21) // OUT
TrapPUTS = uint8(0x22) // PUTS
TrapGETC = uint8(0x23) // GETC
TrapHALT = uint8(0x25) // HALT
)

Expand Down

0 comments on commit f612fe3

Please sign in to comment.