; E06-S04: concrete-to-interface conversion fixture in x128. ; ; Runs under CR=$0E on the E06-S01 value ABI, the E06-S02 dispatch core, and ; the E06-S03 table shape. It checks: ; - assigning a class reference to an interface value writes the object ; pointer and the interface's method table (if_assign mode 1); ; - a null object converts to the canonical four zero bytes; ; - interface-to-interface assignment copies all four bytes verbatim, and a ; zero source pointer is IF_ERR_NULL; ; - a four-byte field and array elements at IF_VALUE_SIZE stride keep the ; pair, and an element address that wraps is IF_ERR_RANGE; ; - pushing an interface value as an argument leaves the object low byte at ; FP+2 and restores the frame stack; ; - a converted value survives the four-byte result area. ; ; The tables below are the shape E06-S03 emits: two-byte method addresses in ; interface slot order, no header. MB_DATA flags 0..5 report the six phases. .setcpu "6502" .include "../../src/common/c128.inc" .include "../../src/common/mailbox.inc" .include "../../src/common/print.inc" .include "../../src/common/frames.inc" .include "../../src/common/interfaces.inc" .import pr_char, pr_cr, pr_text .import frame_init, frame_enter, frame_leave, frame_discard .import if_assign, if_arg_push, if_elem_store, if_elem_load .import if_is_null, if_ret_store, if_ret_load .import if_error .importzp if_obj, if_tab, if_dst, if_src .macro TEXT src, len lda #src ldy #len jsr pr_text .endmacro .macro PHASE n lda #n sta MB_CODE lda #$00 sta MB_CODE+1 .endmacro .macro POINT var lda #var sta tptr+1 .endmacro .macro SETDST var lda #var sta if_dst+1 .endmacro .macro SETSRC var lda #var sta if_src+1 .endmacro .macro OBJECT var lda #var sta if_obj+1 .endmacro .macro TABLE var lda #var sta if_tab+1 .endmacro .macro EXPECT v0, v1, v2, v3 lda #v0 sta e0 lda #v1 sta e1 lda #v2 sta e2 lda #v3 sta e3 .endmacro .macro FAIL_CS bcc :+ jmp @fail : .endmacro .macro FAIL_CC bcs :+ jmp @fail : .endmacro .macro FAIL_NE beq :+ jmp @fail : .endmacro .macro CHK addr, val lda addr cmp #val beq :+ pha lda #addr sta MB_ADDR+1 pla sta MB_CODE lda #$00 sta MB_CODE+1 jmp @fail : .endmacro .segment "LOADADDR" .word $1C01 .segment "EXEHDR" .word basic_end .word 10 .byte $9E .byte "7181" .byte $00 basic_end: .word $0000 .segment "MAILBOX" .res MB_SIZE .segment "ZEROPAGE" tptr: .res 2 .segment "STARTUP" .export test_entry, test_end test_entry: php sei ldx #MB_SIZE - 1 lda #$00 @clr: sta MB_BASE,x dex bpl @clr lda #MB_MAGIC0_VALUE sta MB_MAGIC0 lda #MB_MAGIC1_VALUE sta MB_MAGIC1 lda #MB_PROD_PROGRAM sta MB_PRODUCER lda #MB_ST_RUNNING sta MB_STATUS lda #$00 sta MB_CODE sta MB_CODE+1 sta MB_ADDR sta MB_ADDR+1 sta fail_mask ldx #$05 lda #$00 @flags: sta MB_DATA,x dex bpl @flags lda #$5A sta holder sta holder+1 lda MMU_CR sta saved_cr lda #CR_RAM0_KERNAL sta MMU_CR lda #PR_CLEAR jsr pr_char TEXT title, title_len jsr pr_cr ; 1: a class reference assigns the object/table pair. PHASE 1 OBJECT obj_a_stub TABLE tab_a lda #val_a ldy #1 jsr if_assign FAIL_CS CHK if_error, IF_OK EXPECT obj_a_stub, tab_a POINT val_a jsr cmp_val FAIL_CS lda #$01 sta MB_DATA+0 ; 2: null converts to the canonical four zero bytes. PHASE 2 lda #$00 sta if_obj sta if_obj+1 TABLE tab_a lda #val_b ldy #1 jsr if_assign FAIL_CS CHK if_error, IF_OK EXPECT $00, $00, $00, $00 POINT val_b jsr cmp_val FAIL_CS lda #val_b jsr if_is_null FAIL_CC ; A null value assigned verbatim stays canonical. SETSRC val_b lda #val_c ldy #0 jsr if_assign FAIL_CS POINT val_c jsr cmp_val FAIL_CS lda #$01 sta MB_DATA+1 ; 3: interface-to-interface assignment preserves all four bytes. PHASE 3 SETSRC val_a lda #val_d ldy #0 jsr if_assign FAIL_CS EXPECT obj_a_stub, tab_a POINT val_d jsr cmp_val FAIL_CS lda #$00 sta if_src sta if_src+1 lda #val_d ldy #0 jsr if_assign FAIL_CC CHK if_error, IF_ERR_NULL POINT val_d jsr cmp_val FAIL_CS lda #$01 sta MB_DATA+2 ; 4: one field and three array elements keep their own values. PHASE 4 SETSRC val_a lda #holder_iface ldy #0 jsr if_assign FAIL_CS POINT holder_iface jsr cmp_val FAIL_CS CHK holder, $5A CHK holder+1, $5A SETSRC val_a lda #elems ldy #0 jsr if_elem_store FAIL_CS lda #elems ldy #2 jsr if_elem_store FAIL_CS SETSRC val_b lda #elems ldy #1 jsr if_elem_store FAIL_CS SETDST val_e lda #elems ldy #2 jsr if_elem_load FAIL_CS EXPECT obj_a_stub, tab_a POINT val_e jsr cmp_val FAIL_CS SETDST val_f lda #elems ldy #1 jsr if_elem_load FAIL_CS EXPECT $00, $00, $00, $00 POINT val_f jsr cmp_val FAIL_CS SETDST val_g lda #elems ldy #0 jsr if_elem_load FAIL_CS EXPECT obj_a_stub, tab_a POINT val_g jsr cmp_val FAIL_CS ; $FF00 + 4*200 wraps past $FFFF. SETDST val_g lda #$00 ldx #$FF ldy #200 jsr if_elem_load FAIL_CC CHK if_error, IF_ERR_RANGE lda #$01 sta MB_DATA+3 ; 5: an argument push puts the object low byte at FP+2. PHASE 5 jsr frame_init lda FRAME_SP sta saved_sp lda FRAME_SP+1 sta saved_sp+1 lda #val_a jsr if_arg_push FAIL_CS jsr take_iface FAIL_CS lda #IF_VALUE_SIZE ldx #$00 jsr frame_discard lda FRAME_SP cmp saved_sp FAIL_NE lda FRAME_SP+1 cmp saved_sp+1 FAIL_NE lda #$00 ldx #$00 jsr if_arg_push FAIL_CC CHK if_error, IF_ERR_NULL lda FRAME_SP cmp saved_sp FAIL_NE lda FRAME_SP+1 cmp saved_sp+1 FAIL_NE lda #$01 sta MB_DATA+4 ; 6: a converted value survives the four-byte result area. PHASE 6 lda #val_d jsr if_ret_store FAIL_CS lda #val_c jsr if_ret_load FAIL_CS EXPECT obj_a_stub, tab_a POINT val_c jsr cmp_val FAIL_CS OBJECT obj_a_stub TABLE tab_b lda #val_e ldy #1 jsr if_assign FAIL_CS lda #val_e jsr if_ret_store FAIL_CS lda #val_g jsr if_ret_load FAIL_CS EXPECT obj_a_stub, tab_b POINT val_g jsr cmp_val FAIL_CS lda #$01 sta MB_DATA+5 TEXT ok_msg, ok_len jsr pr_cr lda #MB_ST_PASS sta MB_STATUS lda #$00 sta MB_CODE sta MB_CODE+1 jmp @restore @fail: lda #MB_ST_FAIL sta MB_STATUS TEXT fail_msg, fail_len jsr pr_cr @restore: lda saved_cr sta MMU_CR plp rts ; Reads the four argument bytes at FP+2..FP+5: object, then table. take_iface: lda #$00 ldx #$00 jsr frame_enter bcs @bad ldy #2 lda (FRAME_FP),y cmp #obj_a_stub bne @bad iny lda (FRAME_FP),y cmp #tab_a bne @bad jsr frame_leave clc rts @bad: sec rts ; C=1 when the four bytes at tptr differ from e0..e3. cmp_val: ldy #$00 lda (tptr),y cmp e0 bne @no iny lda (tptr),y cmp e1 bne @no iny lda (tptr),y cmp e2 bne @no iny lda (tptr),y cmp e3 bne @no clc rts @no: sec rts .segment "CODE" ; Method stubs the tables point at, as E06-S03 emits them. m_alpha_a: rts m_beta_a: rts m_alpha_b: rts m_beta_b: rts .segment "DATA" obj_a_stub: .res 2 ; stands in for an allocated object tab_a: .word m_alpha_a, m_beta_a tab_b: .word m_alpha_b, m_beta_b .assert * - tab_a = 2 * 2 * IF_SLOT_SIZE, error, "table size" .segment "RODATA" title: .byte "NOVA128 CONVERT TEST" title_len = * - title ok_msg: .byte "CONVERT OK" ok_len = * - ok_msg fail_msg: .byte "CONVERT FAIL" fail_len = * - fail_msg .segment "BSS" saved_cr: .res 1 saved_sp: .res 2 e0: .res 1 e1: .res 1 e2: .res 1 e3: .res 1 fail_mask: .res 1 val_a: .res IF_VALUE_SIZE val_b: .res IF_VALUE_SIZE val_c: .res IF_VALUE_SIZE val_d: .res IF_VALUE_SIZE val_e: .res IF_VALUE_SIZE val_f: .res IF_VALUE_SIZE val_g: .res IF_VALUE_SIZE holder: .res 2 holder_iface: .res IF_VALUE_SIZE elems: .res 3 * IF_VALUE_SIZE test_end: